1. esparrago_b

    Posted

    esparrago_b
Changes in title
+Apache2.4のMPM prefork/worker/eventの違いを理解する
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,126 @@
+※この記事ではマニュアルに沿って動作の違いを解説したものです。パフォーマンスの差については触れられていないのでご了承ください。
+また、どの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種類があり、同時アクセス数が多ければ、コントローラープロセスが子プロセスをさらに増やして対応します。
+
+![prefork2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227426/61f99418-77b3-8e30-2586-cf37e5229a93.png)
+
+
+以下は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では子プロセス一つに対してサーバースレッドの数だけリクエストを処理できるわけです。
+
+
+
+
+
+![worker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227426/1e196030-39d2-33d2-0f76-0ec82ee1eb23.png)
+
+
+
+起動直後のプロセスの状態は以下の通りです。
+
+```
+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