Edited at

Nginxを使ってファイルアップロードを高速化する方法

More than 3 years have passed since last update.


問題

Webアプリケーションで、大きなファイルのアップロードを受け付けると、リクエストベース(イベントベースじゃない)で作られたWebApplicationでは、 プロセスがその処理にロックされて、他の処理が遅延しパフォーマンスがでなくなります


解決策

ファイルを受け取る 部分をNginxに任せることが出います。

Nginx(イベントベース)のプロセスにファイルのアップロードを担当させて、アップロードしたファイルをApplicationに渡すことができます。ファイルの情報は、ヘッダーで受け取ります。


Before

Client(ファイルをアップロード)


Nginx ()

App (ファイルを受け取って、保存)


After

Client(ファイルをアップロード)


Nginx (ファイルを受け取って、保存)

App (保存されたファイルパスとリクエストを受け取る)


設定

NginxにFileUplodaを行うlocationを指定し、保存先を決めます。

さらに、そのリクエストをproxy_passでbackendのApplicationサーバに送ります。

  location /nignxupload {

limit_except POST { deny all; }

client_body_temp_path /tmp/;
client_body_in_file_only on;
client_body_buffer_size 128K;
client_max_body_size 1000M;

proxy_set_header X-FILE $request_body_file;
proxy_pass http://app_upstream/file/;
}

この設定で、app_upstremにX-FILEヘッダーにてPATHが指定されたリクエストが送られます。

ApplicationをRailsの場合以下のようにします。


controller


class ApplicationController < ActionController::Base
def status
render :text => 'YES'
end

def file
render :text => request.headers['X-File']
end

def app_upload
render :text => params[:file].original_filename
end
end



config/routes.rb

Rails.application.routes.draw do

match '/status' => 'application#status', via: [:get, :post]
match '/file' => 'application#file', via: [:get, :post]
match '/appupload' => 'application#app_upload', via: [:get, :post]
end


機能評価


条件

Haproxy -> Nginx -> App

apache benchで一定の負荷をかけながら、threadで同時にファイルをアップロードします。

Nginx Upload を叩くURLとApp Upload を叩くURLを用意して比較しました。

## 一定の負荷を常にかける

$ab -n 100 -n 1000000 'http://www.example.com/status'

## スレッドでfileをUploadする
$ruby upload_via.rb nignxupload
$ruby upload_via.rb appupload


upload_via.rb

100.times do

Thread.new do
system("curl -H "X-FILE: vagrant" -F file=@README.rdoc -s -D - http://www.example.com/#{ARGV.first}")
end
end

の構成で、Newrelicを使って調べてみた。


結果

App Uploadを使った場合は、プロセスがうめられHaproxyで待たされます(Request Queuing)。プロセスが埋まってしまったため、スループットは出なくなります。

一方Nginx Uploadを使った場合は、NginxがUploadの処理をするためAppプロセスは埋まらずに返信し続けれます。

スクリーンショット_2014-04-14_14_53_26.png

Nginx Upload

スクリーンショット 2014-04-14 14.53.58.png

App Upload

スクリーンショット 2014-04-14 14.54.07.png


セキュリティについて


  • Railsでいうと、protect_from_forgeryをOffにして、nginxからのアクセスについて、何かしらの制限をかける必要がある(ex: internal制限をする)

  • uploadされまくらないように、何かしらの制限を考える必要ある(ex: basic auth)


参考

https://coderwall.com/p/swgfvw