Gracefull shutdownってどういう実装なの?
グレースフルシャットダウンの定義を以下とするなら
- 停止指示後に、新しい接続を受付しない
- 残った処理中の接続が完了するのを待ってから、プロセスを安全に停止する
TCPレイヤでやることとしたらlisten socketをcloseする。 -> 新規接続を受付しない
子プロセスは親から受けたシグナルを元に処理完了で終了するように実装する -> 安全停止
といった流れになる。Nginxをサンプルにみてみる(めちゃめちゃ端折ってます)
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) { for ( ;; ) { // SIG_QUITを受け取った子プロセスは以下に入る if (ngx_quit) { ngx_quit = 0; ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "gracefully shutting down"); ngx_setproctitle("worker process is shutting down"); if (!ngx_exiting) { ngx_exiting = 1; // 終了フラグを立てる ngx_set_shutdown_timer(cycle); // shutdownタイマーを設定する ngx_close_listening_sockets(cycle); // リスニングソケットをcloseする ngx_close_idle_connections(cycle); // アイドルコネクションをcloseする } } // 終了フラグが立っているので以下に入る if (ngx_exiting) { if (ngx_event_no_timers_left() == NGX_OK) { // ngx_event_no_timers_leftはアクティブな接続がある限りはOKにならない ngx_worker_process_exit(cycle); // 終了関数を呼び出す } } } }
ngx_event_no_timers_leftがactiveな接続を監視してなければプロセスが終了する。読んでいて気づいたがこれはバックエンドのアプリのどこかで刺さった場合はNginx自体のこの処理もTimeoutを設定してなければ永遠に動かない実装の模様。そもそもそんなのアプリ側でなんとかしろよって話だけどいつかハマりそう。ngx_event_no_timers_left
はこの辺
ngx_int_t ngx_event_no_timers_left(void) { ngx_event_t *ev; ngx_rbtree_node_t *node, *root, *sentinel; sentinel = ngx_event_timer_rbtree.sentinel; root = ngx_event_timer_rbtree.root; if (root == sentinel) { return NGX_OK; } for (node = ngx_rbtree_min(root, sentinel); node; node = ngx_rbtree_next(&ngx_event_timer_rbtree, node)) { ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer)); if (!ev->cancelable) { return NGX_AGAIN; } } return NGX_OK; }