実際の所、X-SendfileやX-Accel-Redirectは早いのか?
2018/04/07
少し前に「X-SendFile、X-Accel-Redirectの使い方」と言う記事を書きました。
この中で「X-SendfileやX-Accel-Redirectを使用するとほんの少し早い」とか書いてありますし、実際ほんの少し早いのですが「どれくらい早い」と言う実測データを求める方も居ると思われるので計測してみました。
スポンサーリンク
前提
仮想環境の上のUbuntu 16.04にインストールされたnginxとApache、比較用としてPHPと直接送信した場合を測定します。
各バージョンは以下の通り。
- Apache:2.4.18(mpm_event)
- nginx:1.10.0
- PHP:7.0.8(fpm)
- curl:7.47.0
測定用のファイルは1Gのnullファイル(dd if=/dev/zero of=path bs=1G count=1)
受信側は仮想環境の上の別のUbuntu 16.04からcurlを利用してGETリクエストを送ります。(curl -o /dev/null URL)
送信プログラム
PHPで送信する場合は以下のコードを利用。
<?php
header('Content-Type: octet-stream');
// 出力バッファを無効化
while (ob_get_level()) {
ob_end_flush();
}
flush();
// ファイル送信
readfile('/path/to/file');
X-Sendfile及びX-Accel-Redirectで送信する場合は以下のコードを利用
<?php
header('Content-Type: octet-stream');
header('X-Sendfile: /path/to/file');
測定
実際に測定してみよう。
普通の状態
とりあえず、普通に送信した(サーバ上のファイルに直接アクセスした)時の速度を確認しておきましょう。
Apache:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1024M 100 1024M 0 0 107M 0 0:00:09 0:00:09 --:--:-- 112M
nginx:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1024M 100 1024M 0 0 100M 0 0:00:10 0:00:10 --:--:-- 101M
だいたい秒100MBくらいですね、今回はベストのスコアを載せているので誤差でApacheが早いですが、複数回測定して平均的に速かったのはnginx
まぁ、早い/遅いったって、誤差レベルの違いだけど。(nginxが強みを発揮するのは高負荷時)
PHPで送信
次に、PHPで送信した場合
Apache:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1024M 0 1024M 0 0 104M 0 --:--:-- 0:00:09 --:--:-- 104M
nginx:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1024M 0 1024M 0 0 66.9M 0 --:--:-- 0:00:15 --:--:-- 82.4M
Apacheは殆ど劣化が生じていない。
しかしnginxの方は大幅な性能劣化が発生、と言うより早い時と遅い時の振れ幅が大きくなった感じ(多分だがphpから送信されたコンテンツを一度nginx側で受け止めているからだろう、と予想)
Apacheが動的コンテンツに強いと言う話はどうやら本当みたいで……対するnginxは仕組み上そもそも動的コンテンツの扱いが苦手
X-Sendfile、X-Accel-Redirectで送信
PHPとサーバ送信のいいとこどり、X-SendfileやX-Accel-Redirectで送信してみよう。
Apache:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1024M 100 1024M 0 0 107M 0 0:00:09 0:00:09 --:--:-- 115M
nginx:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1024M 100 1024M 0 0 107M 0 0:00:09 0:00:09 --:--:-- 110M
サーバ側でやっているから当たり前でしょうが、性能劣化なし、Apache、nginx共に最速で配信しています。
これを利用すれば、巨大なファイルにアクセス制限(例:購入者限定など)を掛けつつ、高速にファイルを配信する事が可能です。
これを使うと?
ファイルを高速に配信出来る、が、それ以上に様々なメリットがある。
余計なプロセスを使わない
例えば、PHPであればphp-fpmがあり、それらは丁度、Apacheのpreforkと同じような動作をします。(事前にいくつかのプロセスを起動する)
すなわち、プロセス数の上限があるわけです。
巨大なファイルをプログラム側で送信すると、ただファイルを送るだけの処理にPHPのプロセスが1個占有されてしまいます。
そうすると、結果として全体が捌けるアクセス数がプロセス1個分だけ減ってしまう訳です。
X-Sendfileを使えば、ヘッダさえ送り出してしまえばそこから先はサーバのお仕事なので、空いたプロセスが次の処理を捌く事が出来ます。
「PHPでのお話」みたいに書きましたが、別にこれはPHPに限らないと思いますよ。
おススメの使い方
おススメの使い方と言うか、ただのオブジェクト指向と抽象化の話でしょうけど……
ファイル配信の際に「PHP」「X-Sendfile」「X-Accel-Redirect」を切り替え出来ると便利でしょう。
要は抽象化ですが、「ファイル送信クラス」みたいなのを用意し、その中で「PHPからの送信」「X-Sendfile」「X-Accel-Redirect」を切り替える(抽象化してしまう)と楽かと思われます。
そうすれば、切り替えの処理は「ファイル送信クラス」に追い出してしまえるので、「ファイル送信クラス->send("/path/to/file")」みたいにして共通の操作で送信できます。
(X-Accel-Redirectは特殊な挙動で案外使い勝手が悪いのでその辺りは要注意)
とにもかくにも
目立ったデメリットみたいなものがないので、使える状況では積極的に使っていくと良いでしょう。
この機能、案外知らない人……多そうなので。