純規の暇人趣味ブログ

広くて浅い知識の湖

実際の所、X-SendfileやX-Accel-Redirectは早いのか?

      HimaJyun

少し前に「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は特殊な挙動で案外使い勝手が悪いのでその辺りは要注意)

とにもかくにも

目立ったデメリットみたいなものがないので、使える状況では積極的に使っていくと良いでしょう。

この機能、案外知らない人……多そうなので。

 - プログラミング ,