dshimizu/blog/alpha

とりとめのないITブログ

Go の Gin で Graceful shutdown するサンプルを動かしてみた

はじめに

Go の Gin を使ってみて、 Graceful shutdown するにはどうするだろうと調べたメモです。

目次

環境

go 1.20 で試してます。

% go version
go version go1.20 linux/amd64

サンプル

Gin のドキュメントにサンプルの記載がありました。

gin-gonic.com

また、GitHub にもサンプルがありました。

github.com

ほぼサンプル通りですが適当なディレクトリ配下に以下のような main.go を作成&配置して試してみます。

package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
)

func main() {
    //
    ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
    defer stop()

    router := gin.Default()
    router.GET("/hello", helloHandler)

    srv := &http.Server{
        Addr:    ":8080",
        Handler: router,
    }


    // ゴルーチンで http サーバーを起動させる
    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen: %s\n", err)
        }
    }()
    <-ctx.Done()

    stop()
    log.Println("shutting down gracefully, press Ctrl+C again to force")

    // Background()コンテキストを生成する。
    // 指定された時間内(ここでは5秒)に操作が完了しなかった場合にWithTimeout()でタイムアウト処理を行う
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("Server Shutdown:", err)
    }

    log.Println("Server exiting")
}

func helloHandler(c *gin.Context) {
    // テストのため 3 秒待機させる
    time.Sleep(3 * time.Second)
    c.String(http.StatusOK, "Welcome Gin Server")
}

作成後にGoモジュールの初期化を行います。

% go mod init gin-example
go: creating new go.mod: module gin-example
go: to add module requirements and sums:
    go mod tidy

必要なモジュールをインストールしまうs。

% go mod tidy

試す

起動します。

以下のような出力がなされます。

% go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /hello                    --> main.helloHandler (3 handlers)

別ターミナルでリクエストしてみます。

% curl localhost:8080/hello
Welcome Gin Server%   # 3秒後に出力される

curl 実行直後にCtrl + Cによる中断を行います。 go run main.go の出力は以下のように5秒待ってからシャットダウンされました。

[GIN] 2023/04/27 - 16:22:57 | 200 | 10.000212977s |       127.0.0.1 | GET      "/hello"
^C2023/04/27 16:22:23 Shutdown Server ...    # Ctrl + Cによる中断 

2023/04/27 16:22:28 timeout of 5 seconds.
2023/04/27 16:22:28 Server exiting

リクエスト時に10秒待つようにしてみます。

func helloHandler(c *gin.Context) {
         // テストのため 10 秒待機させる
    time.Sleep(10 * time.Second)
    c.String(http.StatusOK, "Welcome Gin Server")
}

別ターミナルでリクエストしてみます。

% curl localhost:8080/hello
curl: (52) Empty reply from server    # 先に中断が実行されるため5秒後に左のような出力がなされる

この場合リクエストが中断されました。

[GIN] 2023/04/27 - 16:22:57 | 200 | 10.000212977s |       127.0.0.1 | GET      "/hello"
^C2023/04/27 16:23:09 Shutdown Server ...
2023/04/27 16:23:14 Server Shutdown:context deadline exceeded
exit status 1

まとめ

Go の Gin で Graceful shutdown するサンプルを動かしてみました。 net/http でも試してみたいと思います。

参考