sygを使用したgraceful shutdown serverパターン

github.com/nasa9084/syg を使用すると、手軽にシグナルとコールバック関数のマッピングを行うことができます1 これを使用し、SIGINTを受けてgraceful shutdownできるHTTPサーバを実装してみます。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package app import ( "context" "net/http" "os" "time" "github.com/nasa9084/syg" ) type Server struct { server *http.Server closed chan struct{} } func NewServer() *Server { http.HandleFunc("/", longlongHandler) return &Server{ server: &http.Server{ Addr: ":8080", }, closed: make(chan struct{}), } } func (s *Server) Run() error { // os.Interrupt = syscall.SIGINT cancel := syg.Listen(s.shutdown, os.Interrupt) defer cancel() err := s.server.ListenAndServe() <-s.closed return err } func (s *Server) shutdown(os.Signal) { s.Shutdown(context.Background()) close(s.closed) } func longlongHandler(w http.ResponseWriter, r *http.Request) { // なんか長い処理のつもり time.Sleep(10 * time.Second) w.Write([]byte("hello")) } mainからは以下の様に呼びます。 ...

2018-03-10 · nasa9084

Golang: 手軽にシグナルをListenしてcallback関数を呼ぶ

Go言語でシグナルを取り扱いたい場合、osパッケージおよびos/signalパッケージ、syscallパッケージを使用します。 具体的には、以下のようにします。 1 2 3 4 5 6 7 8 9 10 11 12 13 func main() { sigCh := make(chan os.Signal, 1) doneCh := make(chan struct{}) signal.Notify(sigCh, syscall.SIGINT) go func() { sig := <-sigCh fmt.Println(sig) // (1) close(doneCh) }() <-done } 実際には、(1)の様に受け取ったシグナルを出力するだけではなく、何らかの処理を行うことになるでしょうし、goroutineのリークを避けるためにシグナルの待受をキャンセルする必要が有りますから、contextを使用してfor-selectループを書くことにもなるでしょう。 例として、HTTPサーバをシャットダウンするような処理を考えます。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 func main() { sigCh := make(chan os.Signal, 1) doneCh := make(chan struct{}) ctx, cancel := context.WithCancel(context.Background()) signal.Notify(sigCh, syscall.SIGINT) s := &http.Server{ Addr: ":8080", Handler: http.DefaultServeMux, } go func() { for { select { case sig := <-sigCh: sig := <-sigCh s.Shutdown(context.Background()) close(doneCh) case <-ctx.Done(): return } } }() if err := s.ListenAndServe(); err != http.ErrServerClosed { log.Println(err) cancel() return } <-doneCh } シグナルを受け取って、関数の呼び出し(ここではs.Shutdown())をしたいだけなのに、チャンネルを作って、goroutineを立ち上げて、となんとも大仰です。 goroutineで呼び出す関数の中でfor-selectループを使っているため、行数も長くなってしまっています。 ...

2018-03-06 · nasa9084