使用go-svc安全退出程序

阅读 NSQ 源码时,看到NSQ使用了 go-svc 来启动nsq的相关程序,使得在程序退出的时候可以做一些释放资源等收尾工作。
使用起来非常简单,定义一个Service,实现其 svc.Service中的接口即可。

简单使用

package main

import (
    "fmt"
    "github.com/judwhite/go-svc/svc"
    "log"
    "syscall"
    "time"
)

type program struct {
}

func main() {
    prg := &program{}
    if err := svc.Run(prg, syscall.SIGUSR1, syscall.SIGINT, syscall.SIGTERM); err != nil {
        log.Fatal(err)
    }
}

func (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.....")
    return nil
}

注意事项

1.Start方法中不能只直接阻塞,需要在Start方法中新开goroutine去写需要阻塞的代码。
2.svc.Run()方法的第二个参数可以指定需要程序监听的信号,默认情况下不指定的话,默认会监听 SIGINT和 SIGTERM两个。根据具体需要进行指定,比如本例中还监听了SIGUSR1.

实现原理

其实go-svc代码实现很简单,使用了标准库中的os/signalNotify方法。
下面的代码截取于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) {}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容