go语言 go-svc包
阅读 NSQ 源码时,看到NSQ使用了 go-svc 来启动nsq的相关程序,使得在程序退出的时候可以做一些释放资源等收尾工作。
使用起来非常简单,定义一个Service,实现其 svc.Service中的接口即可。
使用起来非常简单,定义一个Service,实现其 svc.Service中的接口即可。
简单使用:
func main() { prg := &program{} //运行程序 if err := svc.Run(prg, syscall.SIGINT, syscall.SIGTERM); err != nil { log.Fatal(err) } } //初始化方法 func (p *program) Init(env svc.Environment) error { fmt.Println("init.....") return nil } //启动方法 func (program) Start() error { fmt.Println("start.....") fmt.Println(syscall.Getpid()) go func() { ticker := time.NewTicker(2 * time.Second) for t := range ticker.C { fmt.Println("tick at", t) } }() return nil } //结束方法 func (program) Stop() error { fmt.Println("stop.....")
注意事项
1.Start方法中不能只直接阻塞,需要在Start方法中新开goroutine去写需要阻塞的代码。
2.svc.Run()方法的第二个参数可以指定需要程序监听的信号,默认情况下不指定的话,默认会监听 SIGINT和 SIGTERM两个。根据具体需要进行指定,比如本例中还监听了SIGUSR1.
实现原理
其实go-svc代码实现很简单,使用了标准库中的os/signal
的Notify
方法。
下面的代码截取于judwhite/go-svc/svc/svc-other.go:
// Run runs your Service. // // Run will block until one of the signals specified in sig is received. // If sig is empty syscall.SIGINT and syscall.SIGTERM are used by default. func Run(service Service, sig ...os.Signal) error { env := environment{} if err := service.Init(env); err != nil { return err } if err := service.Start(); err != nil { return err } if len(sig) == 0 { sig = []os.Signal{syscall.SIGINT, syscall.SIGTERM} } signalChan := make(chan os.Signal, 1) signalNotify(signalChan, sig...) //signalNotify方法其实就是 signal.Notify 方法 //var signalNotify = signal.Notify <-signalChan return service.Stop() }
标准库里面的 os/signal中的 Notfiy方法签名如下:
func Notify(c chan<- os.Signal, sig ...os.Signal) {}
其实还有个开源包 endless 也是做类似的事情,大家有兴趣可以看看。