Passenger+Railsでファイルダウンロードを三倍早くするアレの話

 
結論:x_sendfileを使う。
 

調査内容

Passenger(mod_rails | mod_rack)でApacheのx_sendfileが正常に動作するか。
 

Passengerについて

Apacheアプリケーションサーバを介さずにRailsを動作させる拡張モジュール。
Rackも動作します。構造としては、mod_rubyと違い、ApacheRailsRuby)は
取り込まないようです。PassengerSpawnサーバというRubyで動くサーバがApache
のWorkerProcessと連携する構造になっており、そのSpawnサーバで、Railsと、
アプリケーションが二層に渡ってキャッシュされる仕組みになっています。
 
以下のサイトに、日本語訳の技術的な概要について乗っています。
http://lab.koshigoe.jp/en2ja/passenger/Architectural%20overview.html
 
また、ApachePreforkにしか対応していないと記述されていますが、最新のバー
ジョン(2.0.3)ではApacheWorkerにも対応しています。
 

RubyEnterpriseEditionについて

copy-on-writeフレンドリなRuby処理系です。Pushion社がRuby1.8.6(p111)を独
自に改良し、パッケージングしたものです。
Passengerでは、こちらの処理系への切り替えも簡単に行えるように設計してあ
ります。(設定ファイルを書き換えるだけで動作します)
この処理系を使用して、Passengerを利用すると、Passenger自体の動作が大きく
変わります。具体的にはPassengerSpawnサーバ内でforkする前にGCを開始したり、
大きなライブラリ類を親プロセスで前もって読み込む事も行います。
この事で、Request/secは1.2倍向上、使用メモリサイズは0.7倍に縮小されたそうです。
http://www.rubyenterpriseedition.com/comparisons.html
 

x_sendfileについて

ファイルをやり取りする際、Webサーバとクライアントを直接やりとりさせる事
ができます。スクリプト側で巨大なファイルを読み込まなくても済みます。
元々、lighttpdの機能でしたが、Apacheでもmod_xsendfileというモジュールで
実装させれています。
 
mod_xsendfile
http://tn123.ath.cx/mod_xsendfile/
 

Railsでのx_sendfileの動かし方

Rails2.1.0以下のバージョンではx_send_fileというプラグインを使うと簡単に
使用することができます。
http://john.guen.in/past/2007/4/17/send_files_faster_with_xsendfile/
 
Rails2.1.0からは sendfile メソッドのオプションとして、標準で実装されて
います。
例:

send_file '/temp/test.jpg', :type => 'image/jpeg', :disposition => 'inline' , :x_sendfile => true

 

ベンチマーク

以下の環境でベンチマークを取得しました。
 

マシンスペック

OS: linux 2.6.24
CPU: Intel Core 2 1.83GHz
Memory: 2GB
 

各バージョン

Apache : 2.2.8
Passenger : 2.0.3
Ruby on Rails : 2.1.0
 

測定方法

1.8MBの画像を表示するアプリケーションを作成
ab -n 1000 -c 100 ...
で負荷をかけ、Apacheの状態を計測します。
 

実コード
class StreamController < ApplicationController
  def xsendfile
    send_file '/home/nari/private/server/rails/sample/public/images/test.jpg', :type => 'image/jpeg', :disposition => 'inline' , :x_sendfile => true
  end

  def sendfile
    send_file '/home/nari/private/server/rails/sample/public/images/test.jpg', :type => 'image/jpeg', :disposition => 'inline'
  end
end

 

通常の画像表示

Time taken for tests: 18.383565 seconds
Requests per second: 54.40 [#/sec] (mean)
Transfer rate: 96570.82 [Kbytes/sec] received
 

xsendfileでの画像表示

Time taken for tests: 6.675402 seconds
Requests per second: 149.80 [#/sec] (mean)
Transfer rate: 266763.10 [Kbytes/sec] received
 

比較

それぞれ約3倍程度、性能向上しました。
 

結論

x_sendfile使おう