22
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Express (Node.js) の Graceful shutdown

基本的な実装の仕方と、実装した場合 / しなかった場合、で実際にどういう動作をするか〜、について書きます。

Linux, Node.js 12.13.0, での話だけをします。

Graceful shutdown ?

Express (Node.js) に限りませんが、Web サーバーを停止する際、クライアントから接続中のリクエスト (リクエスト受付してまだレスポンスしていない接続) はどうなるでしょうか?

Graceful shutdown とは一般的に以下の停止を指します。

  • 停止指示後に、新しい接続を受付しない
  • 残った処理中の接続が完了するのを待ってから、プロセスを安全に停止する

SIGNALs

そもそも Web サーバープロセスはどうやって停止するかというと、 SIGNAL を用いて停止します。

具体的には下表の通り、コマンド等によって SIGNAL を送信できます。

SIGNAL kill command Linux Terminal Kubernetes 実装依存 説明
SIGHUP kill -SIGHUP {pid} -- -- Yes プロセスが端末から切断された際のシグナル
SIGINT kill -SIGINT {pid} CTRL + C -- Yes 割り込み
SIGKILL kill -SIGKILL {pid} -- SIGTERM 送信から30秒後に送信 NO プロセスの実装に依存しないOSからの強制終了
SIGTERM kill {pid} -- 最初に送信 Yes プロセスの終了

最近では、プロセスを Docker コンテナ化して、 Kubernetes 等のプラットフォーム上で動かす事が多いと思います。

例えば Kubernetes では、プロセス (Docker コンテナ) を終了する際は、まず SIGTERM が送信され、30秒待ってもプロセスが終了しない場合、最終的に SIGKILL が送信されます。

Kubernetes - Pods - Termination of Pods

実装例

URL /sleep と、 SIGNAL SIGTERM での Graceful shutdown を Express で実装しました。

index.js
const express = require('express');

const SLEEP_MSEC = 30 * 1000;
const app = express();

app.get('/sleep', (req, res) => {
  setTimeout(() => res.send('OK'), SLEEP_MSEC);
});

const server = app.listen(3000, () => console.log('Example app listening on port 3000!'));

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Process terminated.')
  })
});

実行します。

$ node index.js

Example app listening on port 3000!

ブラウザで以下の URL にアクセスすると、30秒待たされた後 OK と表示されます。

[未実装で] 実際に停止してみる

まずは 未実装の状態で どう動作するか検証します。

Express サーバーを起動した後、ブラウザで http://localhost:3000/sleep にアクセスします。
まだリクエストの処理中に SIGNAL を送信します。

(1) SIGHUP

シグナルを送信でプロセスが即座に停止。

kill -SIGHUP {pid}
Hangup: 1

(2) SIGINT

シグナルを送信でプロセスが即座に停止。

kill -SIGINT {pid}
(terminal 上の出力はなし)

(3) SIGKILL

シグナルを送信でプロセスが即座に停止。

kill -SIGKILL {pid}
Killed: 9

(4) SIGTERM

シグナルを送信でプロセスが即座に停止。

kill {pid}
Terminated: 15

[SIGTERM 実装で] 実際に停止してみる

実装していない SIGTERM 以外のシグナルは省略。

(5) SIGTERM

ブラウザで http://localhost:3000/sleep にアクセスする。

応答がすぐには帰ってこず、ローディング状態になります。

シグナルを送信します。

kill {pid}

プロセスは実行されたまま、無反応。

(terminal 上の出力はなし)

ブラウザから新しいタブを開いて http://localhost:3000/sleep に追加でアクセスする。

TCP 接続が拒否され、ブラウザ上に「正常に接続できませんでした」と表示される。

1度目のブラウザからのリクエストが 30 秒後に正常に応答が返り、プロセスが終了する。

Process terminated.
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
22
Help us understand the problem. What are the problem?