Fusicきばんブログ

Fusic基盤ユニットの非公式技術ブログ

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

原因

さすがにこの結果はおかしいだろうと原因を調査したところ、下記の記事を見つけました。

stackoverflow.com

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を指定せずとも同じくらいの結果が得られました。