go svc 优雅的程序启动
在研究nsq的源码的时候看到了svc包的使用,一种优雅的程序启动方式,首先看看它的源码
type Service interface {
Init(Environment) error
Start() error
Stop() error
}
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)
signal.Notify(signalChan, sig...)
<-signalChan
return service.Stop()
}
接下来看下nsq是如何使用的
type program struct {
once sync.Once
nsqd *nsqd.NSQD
}
func main() {
prg := &program{}
if err := svc.Run(prg, syscall.SIGINT, syscall.SIGTERM); err != nil {
logFatal("%s", err)
}
}
func (p *program) Init(env svc.Environment) error {
if env.IsWindowsService() {
dir := filepath.Dir(os.Args[0])
return os.Chdir(dir)
}
return nil
}
func (p *program) Start() error {
...
nsqd, err := nsqd.New(opts)
if err != nil {
logFatal("failed to instantiate nsqd - %s", err)
}
p.nsqd = nsqd
...
go func() {
err := p.nsqd.Main()
if err != nil {
p.Stop()
os.Exit(1)
}
}()
return nil
}
func (p *program) Stop() error {
p.once.Do(func() {
p.nsqd.Exit()
})
return nil
}
可以看出,svc框架也是通过 signal.Notify进行信号的通知停止,在svc.Run函数中可以指定监听的信号,默认监听syscall.SIGINT, syscall.SIGTERM。nsq的program实现了接口Service,并且要注意的是start函数中不能直接阻塞,要新开协程进行阻塞,否则svc.Run信号通知不会起作用。