🐡

Goのechoフレームワークのグレースフルシャットダウンの実装

2023/12/03に公開

記事の内容

Goのechoフレームワークのグレースフルシャットダウンを実装して、実際に処理が中断されないかを実験してみました。

記事を読むと得られるもの

  • Goのechoフレームワークのグレースフルシャットダウンの実装方法

対象読者

  • echoユーザー

記事の長さ

1分で読めます

グレースフルシャットダウンとは

グレースフルシャットダウンとは、処理の途中でシャットダウンが発生したときに、現在実行中の処理が完了してからシャットダウンを実行するための実装です。

もし、グレースフルシャットダウンが実装されていないと、データベースへの登録・変更・削除処理が中断されて、データ不整合が起こる可能性があります。

echoサーバーを実装する

echoサーバーを実装します。

グレースフルシャットダウンなし

普通に、echoサーバーを実装していきます。公式チュートリアル通りに実装を行うと以下の通りになります。 ( https://echo.labstack.com/docs/quick-start )

package main

import (
    "net/http"
    
    "github.com/labstack/echo/v4"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}

グレースフルシャットダウンの挙動がわかりやすいように、Sleepを入れて、Webサーバーへアクセスして5秒後にHello, World!が表示されるようにします。

package main

import (
    "net/http"
    // この行を追加
    "time"
    
    "github.com/labstack/echo/v4"
)

func main() {
    e := echo.New()
    e.GET("/", func(c echo.Context) error {
        // この行を追加
        time.Sleep(5 * time.Second)

        return c.String(http.StatusOK, "Hello, World!")
    })
    e.Logger.Fatal(e.Start(":1323"))
}

これで完了です。

以下のコマンドで、echoサーバーを起動し、curlでアクセスします。

$ go run main.go

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.11.3
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1323

$ curl localhost:1323
# 5秒待つ
Hello, World!

期待通りの挙動を確認できました。次に、curlでアクセスしている最中にechoサーバーを停止します。

$ curl localhost:1323
# 5秒待っている間に、echoサーバーを停止する
curl: (52) Empty reply from server

サーバーを終了した瞬間に、アクセスができないことを確認しました。

グレースフルシャットダウンが実装されていないため、curlリクエストの最中に処理が中断されてしまいました。

グレースフルシャットダウンあり

グレースフルシャットダウンなしの挙動を確認できたので、次にグレースフルシャットダウンを導入したechoサーバーを実装します。

こちらも、echoのチュートリアル通りに実装します。( https://echo.labstack.com/docs/cookbook/graceful-shutdown )

package main

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

	"github.com/labstack/echo/v4"
	"github.com/labstack/gommon/log"
)

func main() {
	// Setup
	e := echo.New()
	e.Logger.SetLevel(log.INFO)
	e.GET("/", func(c echo.Context) error {
		time.Sleep(5 * time.Second)
		return c.JSON(http.StatusOK, "Hello, World!")
	})

	// Start server
	go func() {
		if err := e.Start(":1323"); err != nil && err != http.ErrServerClosed {
			e.Logger.Fatal("shutting down the server")
		}
	}()

	// Wait for interrupt signal to gracefully shutdown the server with a timeout of 10 seconds. 
	// Use a buffered channel to avoid missing signals as recommended for signal.Notify
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt)
	<-quit
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	if err := e.Shutdown(ctx); err != nil {
		e.Logger.Fatal(err)
	}
}

上記ファイルを実行します。

$ go run main.go                                                                                                                                                                                                       ~/Projects/zenn-go-echo-graceful/graceful

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.11.3
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\
⇨ http server started on [::]:1323

$ curl localhost:1323
# 5秒まつ
 "Hello, World!"

期待通りの挙動を確認できました。次に、グレースフルシャットダウンが正常に動作するかどうかを確認するために、curlでリクエスト中にGoサーバーを停止します。

$ curl localhost:1323
# echoサーバーを停止する
"Hello, World!"

curlが完了し、Hello, World!が表示されてからGoサーバーが停止することを確認できました。

途中処理のリクエストが完了してから、正常終了することを確認できました。

サンプルソース

今回実装したファイルを以下のリポジトリに公開してあります。

https://github.com/rara-tan/zenn-go-echo-graceful

note

勉強法やキャリア構築法など、エンジニアに役立つ記事をnoteで配信しています。

https://note.com/ring_belle/membership

Discussion