ftp.jaist.ac.jpではHTTPサーバとしてApache HTTP Server (以降Apacheと略記)を使っています。lighttpdじゃないんですかって? えぇ、違います。SourceForge.netのミラーネットワークに参加するためには、Apacheのmod_rewriteを前提にした複雑なURL書き換え規則を受け入れる必要があるので、Apacheでないとだめなんです。
バージョンはまだ2.0系を使っています。本当は、atomic operationがちゃんと使われている2.2系を使いたいのですが、直ったことになっているこのバグがまだ直っていなくて何かと面倒なので、2.0系に居残っています。
使っているマルチプロセシングモジュール(MPM)はもちろんworkerです。worker MPMのさまざまなパラメータの意味はドキュメントに書いてあるし、設定例もあります。しかし、MaxClientsを設定例の150よりはるかに大きな値、たとえば2048に設定したいときに、ほかの値をどれくらいに設定すればいいのか具体的な指針はありません。ググってもそういうことについて書いてあるページは見つかりません。
仕方ないのでworker MPMのソースコードを読みつつ、試行錯誤しながら値を詰めていきました。実際やったのは、ずいぶん前のことですけどね。
worker MPMのパラメータを設定する上で重要なのが、終了待ちプロセス(terminating process)の存在です。終了待ちプロセスは新しいリクエストを受け付けるのをやめて、処理中のリクエストの処理が終わるのを待ちます。ServerLimitの数に終了待ちプロセスは入りません。最悪の場合、1つの処理中のリクエストを抱えた終了待ちプロセスが、MaxClientsだけ増えることもありえます。このことはドキュメントにも書いてあります。
メモリ消費を抑えるためにworker MPMを選択しているのに、終了待ちプロセスがいたずらに増えるのは好ましいことではありません。worker MPMのパラメータのうち、終了待ちプロセスの数に影響するパラメータの一つはMaxSpareThreadsです。
仮にMaxClientsの2048に対してThreadsPerChildを256、ServerLimitを8とします。MinSpareThreadsは設定例と同じ25とします。MaxSpareThreadsはThreadsPerChildとMinSpareThreadsの合計と同じか大きくないといけません。ここでは最低の281とします。
このサーバはいつも忙しいので、あっさり1900クライアントくらいまで行きます。起動しているプロセス数は、1つのコントロールプロセスをのぞけばもう最大の8です。でも、最後のプロセスでも150くらいスペアスレッドがあるので、ほかのプロセスで処理が終了して少しスペアスレッドができればMaxSpareThreadsの281を超えます。こうなるとコントロールプロセスは最後のプロセスに終了を命じて、このプロセスは100クライアントほどつかんだまま終了待ちになります。
この時点でスペアスレッドは130ほどです。また接続が増えてスペアスレッドが25を切ったら、コントロールプロセスは新しいプロセスを起動します。ServerLimitの数に終了待ちプロセスは関係ないので、終了待ちプロセスがいくついても新しいプロセスが起動されます。
終了待ちプロセスは処理が終われば終了するのですが、このサーバは世界中に大きなファイルのダウンロードサービスを提供しているので、たいていのプロセスが1つや2つ遠い国への巨大なファイルのダウンロードを継続していてなかなか終了しません。気が付いたら終了待ちプロセスが何十個も残ってしまい、仮想記憶を使い果たしてしまいました。
この話は実話です。本当にftp.jaist.ac.jpで一度やりました。実際にはMaxClientsは1024でThreadsPerChildは128だったと思いますが。(後編へ続く)
mpm_event とかが使い物になるようになってくれれば C10K 環境でもバリバリ動くようになるのかなぁ。
返信削除そういえば、mod_rewrite は、"いんちきキャッシュ"でも使ってるから手放せない何かですね。