Help us understand the problem. What is going on with this article?

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした