Goでシグナルハンドリングする

自己紹介

GMOペパボでインフラエンジニアをしているmickeyです。 この記事はGMOペパボ Advent Calendar 2017 の12/21の記事です。 最近、Golangに入門しました。ということで、今日は Golang のことを書いていきます。

Golang でシグナルハンドリングする

今、作っているツールでは、シグナルハンドラを実装する必要がありました。C などではシグナルハンドラを書いたことがあるのですが、その経験と比べて Golang は特殊だな、と感じたので、その点についてまとめていきます。

まず、コードを見ましょう。

func main() {
    fin := make(chan bool)

    go func() {

        sigCh := make(chan os.Signal, 1)
        signal.Notify(sigCh, syscall.SIGINT,
            syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

        t := time.NewTicker(3 * time.Second)

        for {
            select {
            case <-sigCh:
                fin <- true
                return
            case <-t.C:
                log.Println("A")
                // 何かとっても長時間かかる処理
                // シグナルによる割り込みで途中で止めたくない
                time.Sleep(10 * time.Second)
                log.Println("B")
            }
        }
    }()

    <-fin
    log.Print("Interrupted")
    os.Exit(0)
}

特徴的だな、と感じたことを端的に書くと、Golang ではシグナルはチャンネルからやってきます。

sigCh := make(chan os.Signal, 1)

のところで、シグナルを受け取るためのチャンネルを作っています。

signal.Notify(sigCh, syscall.SIGINT,
            syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

で受け取りたいシグナルを登録し、

select {
            case <-sigCh:
                fin <- true
                return

でシグナルの受信を待ち、受信したらfinをtrueにして終了しています selectしているのはシグナルの受信と、timerからの通知の両方を待ちたかったからです。

selectしないと、sigChからメッセージが来るまで、ブロックしてしまい、timerからのメッセージを待てません。

C なんかだと、シグナルハンドラは登録する、というイメージだったのでGolangの、チャンネルからシグナルがやってくるというところが特徴的に感じました。

まとめ

というような感じで、Golang殆ど書いていないんですが、頑張って実装を進めています。

作っているものが形になってきたら、そちらについてもまとめていきたいなと思います。