Goji には graceful shutdown は標準で用意されているのですが、 graceful restart がありません。
調べていたところ Circus というものを見つけたので、以下の記事を参考にやってみました。
Goji でアプリケーションサーバーを書く
まず、 Goji でシンプルなアプリケーションサーバーを書きます。
package main
import (
"fmt"
"net/http"
"github.com/zenazn/goji"
"github.com/zenazn/goji/web"
)
func helloWorld(c web.C, w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, world!")
}
func main() {
goji.Get("/", helloWorld)
goji.Serve()
}
Goji はデフォルトだと 8000 を使うので HTTPie だと以下のようにリクエストを送れます。
(curl など他の HTTP クライアントでも構いません)
$ http GET :8000
HTTP/1.1 200 OK
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Date: Mon, 14 Jul 2015 09:04:21 GMT
Hello, world!
Hello, world! が返ってきました。
Circus 化する
Circus で管理するためにはコードに手を加える必要があります。 Socket の listen は Circus に任せて、アプリケーションは Circus から受け取ったファイルディスクリプタ (FD) を使うからです。
修正後の main
は以下のようになります。
func main() {
goji.Get("/", hello)
fd := flag.Uint("fd", 0, "File descriptor to listen and serve.")
flag.Parse()
if *fd != 0 {
listener, err := net.FileListener(os.NewFile(uintptr(*fd), ""))
if err != nil {
panic(err)
}
goji.ServeListener(listener)
} else {
goji.Serve()
}
}
fd
フラグで Circus から FD を受け取ります。 Listener を作成したら ServeListener()
に渡して listen を開始します。
fd
を受け取らなかった場合は Serve()
でデフォルト Listener を使用します。 (ポート 8000)
Circus でサーバーを管理する
Circus の設定ファイルを書きます。
[circus]
statsd = 1
[watcher:webapp]
cmd = goji-with-circus --fd $(circus.sockets.web)
stop_signal = SIGINT
numprocesses = 1
use_sockets = True
copy_env = True
[socket:web]
host = 0.0.0.0
port = 8000
cmd
が実行されるコマンドです。fd
フラグに渡している circus.sockets.web
が下の方で定義している socket:web
になります。注意点として、 stop_signal
に SIGINT
を設定しないと Goji は graceful shutdown しません。デフォルトでは SIGTERM
が使われるためです。
では実行してみましょう。
$ circusd circus.ini
2015-07-14 09:13:01 circus[3617] [INFO] Starting master on pid 3617
2015-07-14 09:13:01 circus[3617] [INFO] sockets started
2015-07-14 09:13:01 circus[3617] [INFO] Arbiter now waiting for commands
2015-07-14 09:13:01 circus[3617] [INFO] webapp started
2015/07/14 09:13:01.716297 Starting Goji on 0.0.0.0:8000
2015-07-14 09:13:01 circus[3617] [INFO] circusd-stats started
2015-07-14 09:13:01 circus[3621] [INFO] Starting the stats streamer
これで Circus を使う前と同じようにリクエストを送ることができます。
$ http GET :8000
HTTP/1.1 200 OK
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Date: Tue, 14 Jul 2015 09:14:18 GMT
Hello, world!
Circus をモニターする
Circus には 2 つのコマンドラインツールが用意されています。そのひとつが circus-top
です。
$ circus-top
Circus Top
------------------------------------------------------------------------------------
circus
PID CPU (%) MEMORY (%) AGE (s)
4245 (circusd-stats) 2.60 0.10 77.16
3617 (circusd) 0.00 0.10 954.12
1.30 (avg) 0.20 (sum) 954.12 (older)
sockets
ADDRESS HITS
0.0.0.0:8000 0
0 (sum)
webapp
PID CPU (%) MEMORY (%) AGE (s)
4244 0.00 0.00 77.15
0.00 (avg) 0.00 (sum) 77.15 (older)
------------------------------------------------------------------------------------
名前のとおり top
の Circus 版という位置付けです。プロセスの CPU 使用率などを見ることができます。
Circus を操作する
もうひとつは circusctl
です。こちらは Circus に対して様々な操作を行うことができます。
-
watcher の一覧
$ circusctl list circusd-stats,webapp
-
watcher に属するプロセスの一覧
$ circusctl list webapp 4244
-
watcher に属するプロセスの増減
$ circusctl incr webapp ok $ circusctl list webapp 4244,4735 $ circusctl decr webapp ok $ circusctl list webapp 4735
-
お待ちかねの graceful restart
$ circusctl reload ok
また、 circusctl
には REPL も用意されています。
$ circusctl
circusctl 0.12.0
circusd-stats: active
webapp: active
(circusctl) list
circusd-stats,webapp
(circusctl) reload
ok
参考
今回のコードは GitHub にあります。