CakePHP3の負荷テストでの落とし穴
基盤ユニットの貞方(@sadapon2008)です。
CakePHP3のアプリをApacheBenchで負荷テストした際にはまった落とし穴をご紹介します。
前提
- CentOS 7.3 + Apache 2.4 + PHP 7.0.17(remi版のmod_php)
落とし穴:ApacheBenchでCakePHP3の負荷テストをやってみたら全く性能が出ない
非常に簡単な負荷テストとして、静的HTML、素のPHP、CakePHP3.4.3でGETに対して簡単なHTMLを返すだけのものを用意し、 下記のコマンドでApacheBenchを実行してRequests per secondを測ってました。
$ ab -t 10 -c 20 -n 1000000 http://a.b.c.d/
すると、CakePHP3だけ3桁以上性能が悪い結果が出て(´・ω・`)となりました。 CakePHP3のデバッグモードはオフにしています。
- 静的HTML: 約 8,000 req/s
- 素のPHP: 約 7,500 req/s
- CakePHP3: 約 2 req/s
原因
さすがにこの結果はおかしいだろうと原因を調査したところ、下記の記事を見つけました。
PHP 5.4以上ではHTTPのリクエストのConnectionヘッダを指定しないとheader()
が非常に遅くなるとのことでした。
CakePHP3のソースを見てみると↓の箇所で使っているheader()
がまさに該当していました。
https://github.com/cakephp/cakephp/blob/3.4.3/src/Http/ResponseEmitter.php#L144
ワークアラウンド
前述の記事を参考にab -k
を指定してConnection: keep-alive
が有効になるようにしてみました。
$ ab -t 10 -c 20 -n 1000000 -k http://a.b.c.d/
その結果、かなり改善しました!
- CakePHP3 before: 約 2 req/s
- CakePHP3 after: 約 700 req/s
元記事によるとクローラーなど行儀の悪いクライアントはConnectionヘッダが欠落していることもあるとのことなので、例えばApacheなら以下のような設定を行うことで、Connectionヘッダを強制することが可能です。
※ (2017/3/28追記)HTTP/1.1ではConnectionヘッダを指定しない場合はkeep-aliveをデフォルトとするようでしたのでプロトコルのバージョンをHTTP/1.0に絞り込む設定に変更しました。
<If "%{SERVER_PROTOCOL} =~ m#^HTTP/1\.0#i"> SetEnvIfNoCase Connection "^(keep-alive|close)$" APP_CONNECTION=1 RequestHeader set Connection close env=!APP_CONNECTION </If>
↑の設定をすると-k
を指定せずとも同じくらいの結果が得られました。