golang中对信号的处理主要使用os/signal包中的两个方法:一个是notify方法用来监听收到的信号;一个是 stop方法用来取消监听。

一、os/signal基本方法

1、func Notify(c chan<- os.Signal, sig ...os.Signal) 第一个参数表示接收信号的channel, 第二个及后面的参数表示设置要监听的信号,如果不设置表示监听所有的信号。

func main() {
        c := make(chan os.Signal, 1)
        signal.Notify(c)

        s := <-c
        fmt.Println("Got signal:", s)
}
//执行kill pid命令即可获得结果

2、func Stop(c chan<- os.Signal)

func main() {
        c := make(chan os.Signal, 1)
        signal.Notify(c)

        signal.Stop(c)
        s := <-c       //此处阻塞,所以不会执行下面的语句,也就没有输出
        fmt.Println("Got signal:", s)
}

由于signal存入channel中,所以可以利用channel特性,通过select针对不同的signal使得系统或者进程执行不同的操作。

二、实现配置平滑重载

1、其实思路很想简单,我们只需要起一个协程,监视我们定义好的信号,如果接收到信号就重新加载配置,主函数代码如下:

func main() {
        s := make(chan os.Signal, 1)
        signal.Notify(s, syscall.SIGUSR1)
        go func() {
                for {
                        <-s
                        g.ReloadConfig()
                        log.Println("Reloaded config")
                }
        }()
        select {}
}

2、解析配置代码如下:

var (
        cfg *config
        once sync.Once
        cfgLock  = new(sync.RWMutex)
)

func Config() *config {
        once.Do(ReloadConfig)
        cfgLock.RLock()
        defer cfgLock.RUnlock()
        return cfg
}

func ReloadConfig() {
        filePath, err := filepath.Abs("conf.toml")
        if err != nil {
                panic(err)
        }
        config := new(config)
        if _ , err := toml.DecodeFile(filePath, config); err != nil {
                panic(err)
        }
        cfgLock.Lock()
        defer cfgLock.Unlock()
        cfg = config
}

这里我们使用了sync.Once的Do方法,Do方法当且仅当第一次被调用时才执行函数。如果once.Do(f)被多次调用,只有第一次调用会执行f,即使f每次调用Do 提供的f值不同。需要给每个要执行仅一次的函数都建立一个Once类型的实例。这样我们就保证了tomlConfig对象是一个单例模式,只需要解析一次,可以在任何地方调用。

测试结果:

kill -SIGUSR1 pid
Reloaded config

results matching ""

    No results matching ""