※この記事ではマニュアルに沿って動作の違いを解説したものです。パフォーマンスの差については触れられていないのでご了承ください。
また、どのMPMを選べばいいかもアドバイスできてません。期待してクリックしてくれた方すみません。
はじめに
RHEL8.0(CentOS8.0)からは、Apache HTTP Server(以下Apache)のデフォルトマルチプロセッシングモジュール(以下MPM)がpreforkからeventに変更になりました。
eventにすること自体はかなり前から可能だったようですが、このリリースからはyum(というかDNF)でApacheを落としたら最初からMPMがeventになっているってことですね。
MPMとは
Webサーバーたるもの、外部からのリクエストにさらされる運命です。
でも、全てのリクエストをシリアルに処理していったのでは、同時に複数のリクエストが届いた場合に対処しきれません(たぶん)。
ApacheではMPMを使うことで、この問題に対処しています。
Apache 2.4 で選択できるMPM
Apache 2.4系で選択できるMPMには以下の通りです。
- prefork
- worker
- event
どのMPMをロードするかは、設定ファイルに記載します。デフォルトでは/etc/httpd/conf.modules.d/00-mpm.conf
に設定されています。
prefork
起動時にApacheの子プロセスを複数用意することで、予めアクセスに備えておく方式です。
プロセスにはコントローラープロセスと子プロセスの2種類があり、同時アクセス数が多ければ、コントローラープロセスが子プロセスをさらに増やして対応します。
以下はApache起動直後のプロセスの状態です。
StartServers
ディレクティブの初期値が5
のため、5つのプロセスがデフォルトで生成されています。
USER列でrootユーザーに紐づいてるのがコントローラープロセス。
apacheユーザーに紐づいているのが子プロセスです。
# ps aux | grep -e httpd -e USER | grep -v grep
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 16272 0.4 0.4 211060 4792 ? Ss 21:39 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16273 0.0 0.2 211060 2852 ? S 21:39 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16274 0.0 0.2 211060 2852 ? S 21:39 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16275 0.0 0.2 211060 2852 ? S 21:39 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16276 0.0 0.2 211060 2852 ? S 21:39 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16277 0.0 0.2 211060 2852 ? S 21:39 0:00 /usr/sbin/httpd -DFOREGROUND
worker
複数の子プロセスを予め起動しておくという点ではpreforkと同じです。
が、各子プロセスがスレッドを持っている点がpreforkと大きく異なります。
スレッドにはリスナースレッドとサーバースレッドの2種類があります。
一つの子プロセスは一つのリスナースレッドと複数のサーバースレッドをもちます。
リスナースレッドがリクエストを待ち受け、手の空いているサーバースレッドに渡します。
preforkでは子プロセス1つで1つのリクエストしか処理できませんでしたが、workerでは子プロセス一つに対してサーバースレッドの数だけリクエストを処理できるわけです。
起動直後のプロセスの状態は以下の通りです。
ps aux | grep -e httpd -e USER | grep -v grep
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 16168 0.0 0.4 211256 4988 ? Ss 21:33 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16169 0.0 0.2 211004 2828 ? S 21:33 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16170 0.0 0.5 500220 5400 ? Sl 21:33 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16171 0.0 0.5 500220 5400 ? Sl 21:33 0:00 /usr/sbin/httpd -DFOREGROUND
apache 16172 0.0 0.5 500220 5404 ? Sl 21:33 0:00 /usr/sbin/httpd -DFOREGROUND
ここで注目してほしいのがVSZ
(仮想メモリサイズ)列です。workerは子プロセスが複数のスレッドを持つため、preforkの時と比べて子プロセス当たりのメモリサイズが大きくなってます。(起動直後の場合ThreadsPerChild
初期値の25のサーバースレッドを持ちます。)
※ところでいつも起動時にメモリサイズの大きくない子プロセスがいますがなんでかわかりません。教えて偉い人
VSZはこの状態で合計1922920KBです。ただし、子プロセスはデフォルトで25のサーバースレッドを持つため、上記の状態で子プロセス数*25=100スレッドがリクエストを待ち受けていることになります。
起動直後の状態を比較すると以下のようになります。
MPM | 親プロセス数 | 子プロセス数 | 同時リクエスト待ち受け数 | 子プロセスの仮想メモリサイズ | 待ち受け口あたりの仮想メモリサイズ |
---|---|---|---|---|---|
prefork | 1 | 5 | 5 | 1055300KB | 211060KB |
worker | 1 | 4 | 100 | 1711664KB | 17117KB |
メモリ消費量の点ではworkerの方が優れていることがよくわかりますね。
一方、スレッドセーフではないプログラムを載せるときなどはスレッドを使えないので、リクエストごとにプロセスを分けるpreforkを使うことになります。
event
eventはworkerのバリエーションです。
子プロセスが複数のスレッドを持つ点ではworkerと変わりません。プロセスの状態も以下の通り、workerとほぼ同じ。
# ps aux | grep -e USER -e httpd | grep -v grep
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 3771 0.1 0.4 211268 5004 ? Ss 20:31 0:00 /usr/sbin/httpd -DFOREGROUND
apache 3772 0.0 0.2 211016 2836 ? S 20:31 0:00 /usr/sbin/httpd -DFOREGROUND
apache 3773 0.0 0.5 500232 5420 ? Sl 20:31 0:00 /usr/sbin/httpd -DFOREGROUND
apache 3774 0.0 0.5 500232 5420 ? Sl 20:31 0:00 /usr/sbin/httpd -DFOREGROUND
apache 3775 0.0 0.5 500232 5424 ? Sl 20:31 0:00 /usr/sbin/httpd -DFOREGROUND
eventとworkerの違いは、HTTPでのKeepAliveの扱いにあります。
workerの場合、サーバースレッドはレスポンス後もKeepAliveが効いている間手空きになることはありませんでした。
一方eventのサーバースレッドは、レスポンス後はコネクションの管理をリスナースレッドに返してしまいます。そうすることで、このサーバースレッドは手空きのスレッドとして次のリクエストを処理できるわけです。
となると、同時に処理できるリクエストの数はworkerとeventで変わらないけど、eventの方がシリアルに処理していくスピードが速いということになるんでしょうね。
どれを選ぶのか
ここまで引っ張っといて申し訳ないのですが正直event触ったことないので、まだわかんないのですすみません。。。
preforkはメモリを食うので、スレッドセーフでないプログラムを載せるのでない限りworker/eventを選ぶことになると思います。
eventはデフォルトMPMになったことだし、いろいろ検証してみたいですね。
参考URL
MPMについて
https://httpd.apache.org/docs/2.4/ja/mpm.html
httpdプロセスのメモリ使用量について
https://www.atmarkit.co.jp/ait/articles/0509/16/news128_3.html
eventの設定・挙動について
https://qiita.com/rryu/items/5e02ea60e36d7fd956b8