ついカッとなって書いた。
タイトルにもある通り、ngx_bumpylifeはnginxでApache HTTP ServerのMaxRequestsPerChild
に相当する挙動を実現するための拡張モジュールです。
さらに言うとap-mod_bumpy_lifeがやってるようなMaxRequestsPerChild
の値を一定範囲内でランダムにする機能も合わせて提供します。bumpylife
という名前はこちらのモジュールから来ています。
使い方と動作解説
こんな感じで設定します。
http {
bumpylife on;
bumpylife_min 500;
bumpylife_max 800;
...
}
ngx_bumpylife
は以下の3つのディレクティブを提供します。
-
bumpylife
->ngx_bumpylife
を有効/無効にする -
bumpylife_min
-> nginxの各ワーカプロセスが終了するまでに処理するリクエスト数の下限値 -
bumpylife_max
-> nginxの各ワーカプロセスが終了するまでに処理するリクエスト数の上限値
ngx_bumpylife
を有効にするとnginxの各ワーカープロセスはbumpylife_min
とbumpylife_max
の範囲内でランダムに決定された値(ngx_http_bumpylife_limit
)を持つようになります。ワーカープロセスは処理したリクエスト数がngx_http_bumpylife_limit
を越えるとgraceful shutdown modeに移行し、ゆるやかに終了します。nginxはワーカープロセスが終了するとマスタプロセスが勝手に新しいワーカープロセスを起動してくれるので、起動したまま放っておくだけでどんどんワーカープロセスが入れ替わっていく、というわけです。
ただ、複数のワーカープロセスが一斉にgraceful shutdown modeに移行してしまうとリクエストを捌くワーカープロセスが1つもない状態になってしまう可能性があるので、ngx_bumpylife
では共有メモリ上にワーカープロセス間で次に終了させるワーカープロセスのIDを保持することで、ある瞬間にgraceful shutdown modeに移行できるワーカープロセスは常に1つになるようにしています。このため、ワーカープロセスが処理したリクエスト数がngx_http_bumpylife_limit
を越えてもある程度動き続ける場合があります。
例えば、あるワーカープロセスAの処理したリクエスト数がngx_http_bumpylife_limit
を越えても、既にgraceful shutdown modeに移行中でまだ入れ替わってないワーカープロセスBが存在している場合、ワーカープロセスAの入れ替え処理は次のリクエスト処理時まで延期されます。
で、このモジュールって要るの?
まず最初に言っておくと、ほとんどの場合においてnginxにMaxRequestsPerChild
相当の機能は不要です。しかし、以下のようにワーカプロセスが大量のメモリを消費しやすい、あるいはメモリリークが起こりやすい場面では「案外有用かも?」と思ってサクッと作ってみた次第です。
- ImageMagick等の画像変換ライブラリを内包したnginxモジュール(e.g. ngx_small_light)を利用する場面
- ngx_luaやngx_mruby等の軽量スクリプト言語でnginx上にそれなりに大きめのアプリケーションを構築する場面
一プログラマとしてメモリの肥大化やリークを許容するのはなんだか負けた気分ですが、現実問題として外部のライブラリやモジュールでリークしてたりすると調査・解決にとても時間がかかったりするので一種のワークアラウンドとしてはまぁ、ありなのかなと思います。(実際、いろんなHTTPサーバでMaxRequestsPerChild相当の機能が実装されているわけですし)
なお、今のところ自分は使う予定はない。
余談
nginxはイベント駆動やノンブロッキングI/Oといったテクニックを駆使することで、個々のワーカープロセスはシングルスレッドでありながらprefork型のHTTPサーバと比べて非常に少ないプロセス数で大量の同時接続を捌くことができるアーキテクチャになっています。なので、通常はワーカープロセス数はCPUのコア数と同じくらいかその整数倍で十分です。しかし、上記の場面ではnginxが極端にCPUバウンドなワークロードを示す可能性があり、ワーカープロセス数を多めに設定する必要が出てきます。こうなってくるとnginxは徐々にprefork型のHTTPサーバとあまり変わらないワークロードを示すようになります。