【Apache】MPMとスレッド/プロセスを調べる

Apacheのインストールと初期のスレッド数などの設定を簡単に調整する機会があり、
MPMとそのパラメータについて様々な資料を参考した内容をまとめてみました！絵付き！

### 書いていること
・MPMの概要説明
・MPMの設定箇所
・基本パラメータの説明とイメージ

### 書いていないこと
・Apacheのインストール方法
・MPMのチューニングの考え方

# MPMとはそもそもなんなのか
「マルチプロセッシングモジュール」の略で、WEBサーバーであるApacheへのリクエストを「どのように処理するか」を決定する仕組みです。
この設定によって、サーバーのパフォーマンスに影響したり、リソースの利用方法が異なります。mpmにも種類があり、「**prefork**」「**worker**」「**event**」の3つのmpmがあります。

まずはそれぞれの内容、概要を説明します！

### ◼️prefork

- 親プロセスに対して、複数の子プロセスがある
- リクエストを子プロセスで対応する
- リクエストが増えた時：子プロセスを増やして対応する
- 子プロセスごとにリソースを確保するため、メモリ消費量が多くなる傾向がある。
- スレッド非対応のモジュールや古いライブラリ（例: 古いPHPモジュール）との互換性が高い。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287983/7b8787f1-c61e-3710-b4b5-07824ea5e37b.png)


### ◼️worker

- 親プロセスに対して、複数の子プロセスがある
    - さらに子プロセスは一つの**リスナースレッド**を持っている
        - リスナースレッドは複数（デフォルトで25）の**ワーカースレッド**を持っている。
        - リクエストがあると、リスナースレッドで受け取り、ワーカースレッドに振り分ける。
- リクエストが増えた時: 多くのワーカースレッドを効率的に利用し、同時に多数のリクエストを処理。
- メモリ効率が良く、workerプロセスが少ない状態でも多くのリクエストを処理できる。
- ワーカースレッドはKeep-Alive接続を保持するため、リクエストを処理した後、しばらく続きの通信を待ち、動きが固定されるケースがある

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287983/079f13fb-2bdb-ba84-572b-5b8eb0dd4e4c.png)


### ◼️event

- 基本的には**worker**と同じ。
- ただし、Keep-Alive接続はevent専用の処理で管理され、workerよりもワーカースレッドが次のリクエスト処理に迅速に移れる。
    - ワーカーさんでの処理が終わった時、リスナースレッドさんに「自分、次の仕事できます！」と返して、早くアイドル状態に戻れる、というイメージです。
- 高い同時接続効率を実現し、大量アクセスにも強いです。
- 現状の**Apache2.4**のデフォルトmpm。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287983/ebf3523c-66b2-cae0-aa5e-cd0b2c064145.png)


**参考記事**：

https://qiita.com/esparrago_b/items/4f368599aba1a059dbd1

💡なお、利用中のmpmは以下のファイルで確認可能です

```php
cat /etc/httpd/conf.modules.d/00-mpm.conf

# 略
LoadModule mpm_event_module modules/mod_mpm_event.so
```

# MPMのプロセス数、スレッド数の設定箇所

続いて、mpmの処理するサーバーのプロセスやスレッド数の調整について調べました。
※Apacheインストール直後など、存在しない場合は作成します。
デフォルトの場合は以下のようなパラメータです。

```php
cat /etc/httpd/conf.modules.d/10-mpmevent.conf

<IfModule mpm_event_module>
	StartServers             2
	MinSpareThreads          25
	MaxSpareThreads          75
	ThreadLimit              64
	ThreadsPerChild          25
	MaxRequestWorkers        150
	MaxConnectionsPerChild   0
</IfModule>
```

# MPMの各項目の説明

それぞれのデフォルトの記述内容+αを記載しました。

```php
<IfModule mpm_event_module>    
    # 設定可能な子プロセス数の上限。
    # 必要以上に大きな値に設定されると、余計な未使用メモリが割り当てられる。
    ServerLimit               8
    
    # 起動時に自動で生成される子プロセスの数。
    StartServers              5
    
    # リクエストに応答することのできる、アイドルスレッド数のすべてのプロセスの合計の最小数。
    # サーバーが最低限保持しておきたい数とも言えるため、
    # 忙しくなってアイドルスレッドがなくなってきても、この数まで増やされます。
    MinSpareThreads           15
    
    # アイドルスレッドのすべてのプロセス全体の合計最大数。
    # MIn...とは逆に、暇になってアイドルスレッドが大量に余ると、
    # この数までは減らされます。
    MaxSpareThreads           75
    
    # 子プロセスそれぞれに生成されるワーカースレッド数
    ThreadsPerChild           3
    
    # 子プロセスそれぞれのワーカースレッド数の上限
    ThreadLimit               4
    
    # 同時に処理される接続の最大数。全プロセス合計のワーカースレッド総数。
    MaxRequestWorkers         15
    
    # 個々の子プロセスがその存続期間中に処理する接続数の制限
    MaxConnectionsPerChild    1280
    
    # event_mpm 専用の内容。
    # 各子プロセスで受け付ける非同期接続数に影響する
    AsyncRequestWorkerFactor  2 
</IfModule>
```

### AsyncRequestWorkerFactorについて

最後に記載した`AsyncRequestWorkerFactor`は少し分かりづらいのですが、これはワーカースレッドの非同期接続（Keep-Alive 接続など）の処理効率を補正する要素です。

**非同期接続数（1子プロセスあたり）= 
ThreadsPerChild * (AsyncRequestWorkerFactor + 1)**

このような計算式となるため、ワーカースレッド数より多めの数で非同期接続を対応できます。例として、

```php
ThreadsPerChild 25
AsyncRequestWorkerFactor 2
```

の場合、**非同期接続数（1子プロセスあたり）は**

```php
25 * (2 + 1) = 75
```

なので、各子プロセスが 75 の非同期接続を受け付けることが可能…ということになります。

AsyncRequestWorkerFactorを高くすると、1つの子プロセスで処理できる非同期接続数が増加します。が、スレッドが過剰に使用されるリスクがあるため、設定値は負荷テストなどを通じて慎重に調整する必要があります。


以上、あげたパラメータの関連箇所をイメージにしてみました。（細かいので拡大してね）
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287983/9adc3bc6-8df4-72b3-cccb-de74dec41368.png)


**参考記事**

https://httpd.apache.org/docs/2.4/ja/mod/mpm_common.html

https://httpd.apache.org/docs/2.4/ja/mod/event.html

https://qiita.com/rryu/items/5e02ea60e36d7fd956b8#asyncrequestworkerfactor

# MPMの変更反映を確認する方法

`/etc/httpd/conf/httpd.conf` の末尾に、一時的に以下を記述し

```php
<Location /server-status>
    SetHandler server-status
</Location>
```

再起動します

```php
sudo systemctl reload httpd
```

`http://URL/server-status`にアクセスすると、以下のようなスレッドのidle状態などの表示が確認可能です。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287983/44886156-8bf1-b7ff-eb04-7bbd725a8df4.png)

参考：https://httpd.apache.org/docs/2.4/ja/mod/mod_status.html


# 最後に
基本的にこれらの情報は引用先から参照できますが、色々な場所に散らばっていて把握が難しかったので、まとめることで理解が深まったと思います！
Apache初学者さん（ワイもや）の参考になれば幸いです。