Apacheのアクセス数制御検証を可視化しながらやってみた


# 概要
Apacheの設定を手元のVMマシンでごにょごにょしていて、
ふと性能のキャップを外すことはあるけど、意図的にキャップをかけたらApacheとOSから見た時のnetstat状態ってどんな挙動になるのだろうと思ったことをつらつらとチラシの裏レベルで書いていきます。
また、数カ月前に試したことだったのでちょっとうろ覚えのところもあります。
なので、多分一般的な人はほぼ意味がない記事になるかと思います。
また、サーバのセットアップ手順は割愛しています。
すいません、すいません、すいません。

# 検証概要
静的ファイルをGETするだけの単純な負荷試験を実施。

- 受け付ける秒間リクエストは50rps
- ファイルサイズは1KB,2KB,4KB,8KB,16KBの5種類
- 上記の状態でfluent-plugin-dstatで情報をElasticSearchに突っ込んでKibanaで可視化してみる

# 環境
- サーバ：CentOS 6.6 x86_64（VMwarePlayer上）
- Apache：apache 2.2系
- クライアント：Apache Bench, JMeter


# 検証１回目
## 設定値
```lang:httpd.conf
<IfModule prefork.c>
StartServers       50
MinSpareServers    50
MaxSpareServers    50
MaxClients         50
MaxRequestsPerChild 1
</IfModule>
```
## 予想
多分この設定で50rpsになるはず。。。

## 結果
### ファイルサイズ：1k 並列度:180 アクセス数：1000 
cmd：ab -n 1000 -c 180 web01t.local/1k.txt
![ab_1k_180_1000.png](https://qiita-image-store.s3.amazonaws.com/0/70732/ccaf2d6c-1dc5-c784-c52a-1e3934e24652.png)

### ファイルサイズ：2k 並列度:180 アクセス数：1000 
cmd：ab -n 1000 -c 180 web01t.local/2k.txt
![ab_2k_180_1000.png](https://qiita-image-store.s3.amazonaws.com/0/70732/85b4b056-9883-80e4-dc40-01e336dcea34.png)

### ファイルサイズ：4k 並列度:180 アクセス数：1000 
cmd：ab -n 1000 -c 180 web01t.local/4k.txt
![ab_4k_180_1000.png](https://qiita-image-store.s3.amazonaws.com/0/70732/62d43e29-983b-9aa3-710a-2c23028cab54.png)


### ファイルサイズ：8k 並列度:180 アクセス数：1000 
cmd：ab -n 1000 -c 180 web01t.local/8k.txt
![ab_8k_180_1000.png](https://qiita-image-store.s3.amazonaws.com/0/70732/f69c9437-6c14-c258-690e-0272652b53d5.png)


### ファイルサイズ：16k 並列度:180 アクセス数：1000 
cmd：ab -n 1000 -c 180 web01t.local/16k.txt
![ab_16k_180_1000.png](https://qiita-image-store.s3.amazonaws.com/0/70732/bb4997b1-b75f-2140-9a96-f2ef2a3c2109.png)

## 検証１回目の結果
なんかよくわからないけど３２rpsになってしまう。
ではMaxRequestsPerChildを倍に増やすとどうなる？

## 検証２回目
## 設定値
```lang:httpd.conf
<IfModule prefork.c>
StartServers       50
MinSpareServers    50
MaxSpareServers    50
MaxClients         50
MaxRequestsPerChild 2
</IfModule>
```

### ファイルサイズ：16k 並列度:180 アクセス数：1000 
cmd：ab -n 1000 -c 180 web01t.local/16k.txt
![ab_16k_180_1000_2.png](https://qiita-image-store.s3.amazonaws.com/0/70732/16fb2f54-f50c-156d-099e-c4f4447ff907.png)

## 検証２回目の結果
綺麗に３２の等倍である６４rpsになった！
ということはコードの中でプロセス数のキャップがかかっているっぽい気がする。
forkするのはメモリやCPUコストかかるから、
普通はMaxRequestsPerChildを増やして子プロセスが死ぬまでの回数を調整しましょうということなのかな。。。？

# JMeterで負荷試験してみた
Apache Benchだと並列処理が困難なので。。。

## 設定値
```lang:httpd.conf
<IfModule prefork.c>
StartServers       50
MinSpareServers    50
MaxSpareServers    50
MaxClients         50
MaxRequestsPerChild 2
</IfModule>
```

## グラフ説明
- 上段グラフの緑色：Apacheの秒間アクセス数
- 下段グラフの薄紫：netstatのtime_wait
- 下段グラフの濃い紫：netstatのsyn_recv
- 下段グラフの緑：netstatのestablish

## 10rps(time:60 , thread:600)
![10rps.png](https://qiita-image-store.s3.amazonaws.com/0/70732/a462cd71-ba33-9560-169b-5e56ccb71869.png)

## 30rps(time:60 , thread:1800)
![30rps.png](https://qiita-image-store.s3.amazonaws.com/0/70732/9e804442-903e-321c-9c13-0f8aea317f4b.png)

## 60rps(time:60 , thread:3600)
![60rps.png](https://qiita-image-store.s3.amazonaws.com/0/70732/92be0140-6f18-5ec5-7192-92be8ac31465.png)

## 120rps(time:60 , thread:7200)
![120rps.png](https://qiita-image-store.s3.amazonaws.com/0/70732/175ab06d-63f0-e975-a55b-9088c7a48dff.png)

## 180rps(time:60 , thread:10800)
![180rps.png](https://qiita-image-store.s3.amazonaws.com/0/70732/65e7c3db-63ee-c9da-ca0d-2989ac2fda31.png)

# JMeterを使った検証結果
並列度が６０くらいまではおおよそ問題なく捌けていそう。
倍の１２０にした途端タイムアウトが頻発するようになった。
グラフがガタガタなのはホスト機のCPUリソースがカツカツすぎてフリーズしかけてたからだと思います。。。

# ネットワーク状態は？
並列度が60を超えた当たりからSYN_RECVが滞留し始めた。
netstat -anとかでみるとどうやら詰まり始めたらSYN_RECVの数が増加しているようだった。
並列度を上げてSYN_RECVの数がスケールしていくのは、
ESTABLISHに移ろうとしてもプロセスがリクエストを捌ききれなくて状態遷移できないためと予想。
## 参考
- http://qiita.com/kuni-nakaji/items/c07004c7d9e5bb683bc2 
- http://thinkit.co.jp/article/118/2/page/0/2 


# 余談
JMeterで120rps負荷試験をかけたあたりからdstatで「missed 2 ticks」という表示が出て情報が欠けるようになった。
こんな表示あったんですね。。。！
![dstat.png](https://qiita-image-store.s3.amazonaws.com/0/70732/287f5fb8-dab5-8cd1-03fb-e7c59286b47c.png)

# MaxClientsが３３以上はスルーされてしまった件
少しだけコードの中を覗いてみました。
MAX_SPAWN_RATEというのがあるようです。
時間があるときに値を修正してリコンパイルして試してみたいと思います！

```lang:prefork.c
 115     /*
 116      * idle_spawn_rate is the number of children that will be spawned on the
 117      * next maintenance cycle if there aren't enough idle servers.  It is
 118      * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
 119      * without the need to spawn.
 120      */
 121     int idle_spawn_rate;
 122 #ifndef MAX_SPAWN_RATE
 123 #define MAX_SPAWN_RATE  (32)
 124 #endif
```