go语言的信号及其应用

一、signal包

  1、Notify函数

     func Notify(c chan<- os.Signal, sig ...os.Signal)

      说明:Notify函数让signal包将输入信号转发到c。如果没有列出要传递的信号,会将所有输入信号传递到c;否则只传递列出的输入信号。且当c阻塞时(缓冲满了),os的输入信号将被丢弃。

  2、Stop函数

    func Stop(c chan<- os.Signal)

    说明:Stop函数让signal包停止向c转发信号。它会取消之前使用c调用的所有Notify的效果。当Stop返回后,会保证c不再接收到任何信号。

二、signal在Web服务中的应用(优雅的退出服务进程)

  1、当服务接收到外部输入的终止信号时,应该保证相应的资源得到释放。

  2、首先创建信号的处理器,以及信号集结构体,用于对不同信号实现不同处理,代码示例如下:

type handler func(sig os.Signal, arg interface{})  //处理器函数类型
// 信号集结构体
type sigSet struct {
    set map[os.Signal]signalHandler
}
// 注册对应的信号及处理器函数
func (sigs *sigSet) register(s os.Signal, h handler) {
    if _, found := sigs.m[s]; !found {
        sigs.m[s] = h
    }
}
// 对象相应的信号进行处理操作
func (sigs *sigSet) handle(sig os.Signal, arg interface{}) (err error) {
    if _, found := sigs.m[sig]; found {
        sigs.m[sig](sig, arg)  //执行指定的处理函数
        return nil
    } else {
        return fmt.Errorf("no handler for signal %v", sig)
    }
}

  3、停止服务器的入口函数

func StopServer(release func()) {
    sigMap := new(sigSet)
    sigMap.set = make(map[os.Signal]signalHandler)
    
    Handler := func(sig os.Signal, arg interface{}) {
        release() //释放资源,并开始退出程序
        os.Exit(0)
    }
    sigMap.register(syscall.SIGINT, Handler) //注册ctrl+c的外部输入信号,可注册多个其他信号及处理函数
        // 循环等待外部输入信号
    for {
        s := make(chan os.Signal)
        signal.Notify(s) //将外部输入信号都传递给s管道
        sig := <-s        //阻塞,直至s中管道有信号
        err := sig.handle(sig, nil)
        if err != nil {
            fmt.Errorf("unknown signal received %v", sig)
        }
    }
}

  4、主函数的实现,创建一个goruotine来执行服务器停止函数

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    stop := make(chan bool, 1)
    go StopServer( func() {
        service.Stop() //关闭与存储相关的连接资源
        stop <- true
    })
    service.Start() //启动相关web服务
    <- stop         //阻塞,直到stop管道被赋值
    fmt.Println("service is over")
}