Apache MPMとはなんぞやという話

Apache のチューニングにあたって MPM について調査したときのメモ、なんとなくの概要。

Webサーバの実装モデルの話

MPMの話に入る前に、Webサーバの基本的な並行処理のモデルをおさえておきます。

Webサーバに同時に接続するクライアントが1人だけであれば並行処理について考える事項は少ないですが、多くの場合は同時に複数人のクライアントに対応しなければならないかと思います。
そういった複数人のクライアントからのリクエストを並行処理するためのWebサーバの実装モデルがいくつかあるのですが、代表的なものを簡単におさらいします。

マルチプロセスモデル

クライアントからのリクエストごとに fork を行い子プロセスを生成し、子プロセスに処理を委ねる方式です。
プロセスの fork では、メモリ上の親プロセスのアドレス空間を生成した子プロセスのアドレス空間にコピーするため、その分のコストが発生し、遅いと言われています。
リクエストが増えれば増えるほど、子プロセスの数とそれに伴うメモリ消費量も増えていきます(すべての子プロセスがPHPインタプリタおよび関連ライブラリをロードするため)。
メモリ空間がプロセスごとに独立しているためスクリプト言語などを組み込みやすい、後述のマルチスレッドモデルと違い資源の競合について考慮しなくてよい、などの利点があります。

マルチスレッドモデル

クライアントからのリクエストごとにスレッドを生成する方式と、あらかじめスレッドを生成しておくモデルがあります。
マルチプロセスモデルとは違い、プロセスではなくスレッドを使用するため、プロセスの fork の際に発生するようなコピー作業が発生しない(各スレッドはメモリ空間を共有する)ので、プロセスの生成よりもコストが小さいと言われています(メモリ消費量についても同様)。
メモリ空間を共有するので、コンテキスト切り替えの際に発生するメモリ空間の切り替えやそれに伴うキャッシュの削除を省略できるというメリットもあります。
メモリ空間を共有することのデメリットとして、スレッド間での資源の競合を考慮したプログラムを書く必要があり、実装が難しくなりコードも複雑なものになりやすい、などが挙げられます。

イベント駆動モデル

1つのプロセスで複数のリクエストを処理する方式です。
上記2つのモデルでは、クライアントがサーバに接続してレスポンスを受けてサーバとの接続を切る、という一連の流れ1つに対してを1つのプロセス or スレッドが割り当てられ、それぞれのリクエストに対応していました。
イベント駆動モデルでは、リクエスト数に関係なくイベント発生のタイミングで処理を切り替え1つのプロセスがすべてのリクエストを処理します。
プロセスが1つしか無いということは、CPUコアを1つしか活用できないことを意味します。
Nginx などでは、イベント駆動のプロセスをCPUコアそれぞれに起動しておくなどして、この欠点に対応しています。
このモデルは、リクエスト数が増えてもプロセスやスレッドの数が増えることがないので、メモリ消費量やコンテキスト切り替えのオーバヘッドなどのコストを抑える事ができるという利点があります。

ところで MPM って?

MPM は (Multi Processing Module) の略で、Webブラウザからのリクエストを Apache がどのように並行処理するか、という部分の処理をモジュール化したものです。
先にWebサーバのモデルに関して記述しましたが、Apache ではそのようなモデルの中からどれを使用するかをこの MPM によって選択することができます。
Apache そのものにこれらの処理が組み込まれずにモジュール化されていることによって、各々のWebサイト向けにカスタマイズすることが容易になっています。
Apache2.2までは MPM は静的にリンクしなければなりませんでしたが、Apache2.3 からは LoadModule ディレクティブで動的に選択することが可能になりました。

MPM の種類

代表的なものをまとめます。

prefork

マルチプロセスモデルです。
prefork という名の通り、クライアントからリクエストが来る前にあらかじめ一定数の子プロセスを fork して待機させておくことで、fork の回数を減らしてパフォーマンス向上を図ります。
リクエストごとにプロセスが分かれているため、あるプロセスの障害が他のプロセスに影響を及ぼすことが無く、安定した通信を行うことが可能です。

worker

マルチスレッドモデルとマルチプロセスモデルのハイブリッドモデルです。
制御用の親プロセスがいくつかの子プロセスを作成し、子プロセスそれぞれがマルチスレッドで動作します。
スレッド1つが1つのクライアントの処理を担当します。
prefork に比べ生成されるプロセスの数を抑えることができるので、資源の節約が可能です。
スレッドを使用するモデルなので、mod_php などの非スレッドセーフなモジュールを利用する際には使用できません。

event

worker をベースとしたマルチスレッドモデルとマルチプロセスモデルとイベント駆動モデルのハイブリッドモデルです。
KeepAlive の処理を別のスレッドに割り振って通信を処理することによって、パフォーマンスの向上を図っています。
また、クライアントとのネットワークI/Oのみイベント駆動モデルで実装されています。
こちらもスレッドを使用するモデルなので、非スレッドセーフなモジュールを利用する際には使用できません。