2019年9月4日水曜日

go-workers で SIGINT や SIGHUP のシグナルをハンドリングする方法

概要

go-workers でシグナルをハンドリングする方法を紹介します

環境

  • macOS 10.14.6
  • go 1.12.9
    • go-workers dbf81d0b75bbe2fd90ef66a07643dd70cb42a88a

go-workers 内でハンドリングしてくれるシグナル

実は go-workers 内でも特定の Ctrl+c などのシグナルはハンドリングしてくれています

func handleSignals() {
        signals := make(chan os.Signal, 1)
        signal.Notify(signals, syscall.SIGUSR1, syscall.SIGINT, syscall.SIGTERM)

        for sig := range signals {
                switch sig {
                case syscall.SIGINT, syscall.SIGUSR1, syscall.SIGTERM:
                        Quit()
                }
        }
}

この関数はワーカーが Run() した際に goroutine として実行されています
Ctrl+c は syscall.SIGINT になります
やっていることは単純で Quit() という処理を実行してワーカーが正しく終了されるような処理を実行しています

ただ SIGHUP などはハンドリングしていないのでワーカーに SIGHUP を送信すると「Hungup: 1」と表示されて終了してしまいます
しかも go run で実行しているとビルドのプロセスが残ってしまい面倒なことになります
なので他のシグナルも本当はちゃんとハンドリングしてあげる必要がありそうです

SIGHUP をハンドリングしてみる

試しに SIGHUP を追加でハンドリングできるようにしていみます
特にライブラリに手を入れる必要はなくワーカー側でハンドリングするようにすれば OK です

package main

import (
    "os"
    "os/signal"
    "syscall"

    "github.com/jrallison/go-workers"
)

func handleSignals() {
    signals := make(chan os.Signal, 1)
    signal.Notify(signals, syscall.SIGHUP)
    for sig := range signals {
        switch sig {
        case syscall.SIGHUP:
            workers.Logger.Println("sighup")
        }
    }
}

func perform(msg *workers.Msg) {
    c, _ := msg.Args().Int()
    for i := 0; i < c; i++ {
        workers.Logger.Println(i)
    }
    workers.Logger.Println("end")
}

func main() {
    workers.Configure(map[string]string{
        "server":  "localhost:6379",
        "process": "1",
    })
    workers.Process("default", perform, 1)
    go handleSignals()
    workers.Run()
}

handleSignals 関数をそのまま使って syscall.SIGHUP をハンドリングできるようにします
また main() 内で Run() する前に goroutine として関数を実行しておきます

動作確認

ワーカーを起動して SIGHUP シグナルを送信してみましょう

  • go fmt ./... && go build worker.go && ./worker
  • kill -SIGHUP `ps aux | grep './worker' | grep -v grep | awk '{print $2}'`
workers: 2019/09/04 09:15:55.161765 processing queue default with 1 workers. workers: 2019/09/04 09:15:59.230297 sighup

こんな感じで表示されます
またハンドリングしていない場合はプロセス自体が終了していたのですが今度は終了しなくなったと思います

SIGINT を独自にハンドリングしてみる

go-workers 側で SIGINT をハンドリングしていますが自信でもハンドリングしたいケースはあると思います
先程のワーカーの handleSignals 関数に syscall.SIGINT も追加してみます

func handleSignals() {
    signals := make(chan os.Signal, 1)
    signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT)
    for sig := range signals {
        switch sig {
        case syscall.SIGHUP:
            workers.Logger.Println("sighup")
        case syscall.SIGINT:
            workers.Logger.Println("sigint")
        }
    }
}

動作確認

  • go fmt ./... && go build worker.go && ./worker
  • kill -SIGINT `ps aux | grep './worker' | grep -v grep | awk '{print $2}'`
workers: 2019/09/04 09:32:23.858982 processing queue default with 1 workers. workers: 2019/09/04 09:35:47.852373 sigint workers: 2019/09/04 09:35:47.852375 quitting queue default (waiting for 0 / 1 workers).

ちゃんと SIGINT を独自でハンドリングできました
が、独自のハンドラを抜けてライブラリ側のハンドラーにも行ってしまうようでワーカーは終了してしまいました
たぶん golang の仕様だと思うのですがシグナルのハンドラが複数 goroutine で動いている場合はすべての goroutine がシグナルをキャッチできるんだと思います

なので使い方としては SIGINT の終了前に何か処理をしたい場合に使う感じかなと思います

最後に

go-workers でシグナルをハンドリングする方法を紹介しました
コードを見ると AfterQuit という関数がコメントアウトされていたので始めはミドルウェア的な使い方でハンドリングできるのかなと思ったのですがどうやらダメそうです
今回紹介した方法はライブラリ側でシグナルハンドリングが行われる前に何かする方法になります
なのでライブラリ側で処理した後に何かしたい場合は直接コードを書き換えるしかなさそうで

0 件のコメント:

コメントを投稿