Thursday 24 May 2012

SinatraとHerokuでフレキシブルな静的ファイルサーバ構築

SinatraとHerokuでフレキシブルな静的ファイルサーバ構築をしてみました。実際役に立つかどうか?というのは、用途によります。

SinatraもRailsもgemなどを使ってインストールさせ、サーバを起動すれば /public以下のフォルダをかってに拾ってくれるので、単純なhtmlのサイトであればお手軽に無料でサイトが作成できます。(無料でサイトを作成するという意味であればWordpressやFacebook, Google Pageなどいくらでも便利なつーるはあり、そちらの方が断然便利だと思います。)

Sinatraならではのフレキシブルな点としてはアプリケーションに

get "/folder_name/*"
  protect!
  File.read("/public/index.html")
end

などとすれば特定のフォルダを簡単にBasic認証で保護できます。

また、Amazon S3の上にファイルを置いて、下のようにすれば特定の(この場合は東北アジアのサーバに置いた)バスケットに見せたいファイルをアップロードすれば普通の静的ファイルサーバの様に見せてくれます。jpgでもhtmlでもpdfでも問題無いはずです。


require 'rubygems'
require 'sinatra'
require 'aws/s3'
include AWS::S3

helpers do
  def connect_aws_s3
    DEFAULT_HOST.replace "s3-ap-northeast-1.amazonaws.com"
    connection = Base.establish_connection!(
      :access_key_id     => 'YOUR-KEY-ID',
      :secret_access_key => 'YOUR-SECRET-ACCESS-KEY'
    )
    @bucket = Bucket.find('sinatra-aws-s3-test')
  end

  def protect!(user_name, passwd)
    unless authorized?(user_name, passwd)
      response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
      throw(:halt, [401, "Not authorized\n"])
    end
  end

  def authorized?(user_name, passwd)
    @auth ||=  Rack::Auth::Basic::Request.new(request.env)
    username = user_name
    password = passwd
    @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == [username, password]
  end
end

get %r{.+} do

  protect!("username", "password") if request.path.match(/^\/folder_name/)

  connect_aws_s3

  file_path = request.path
  file_path += "index.html" if file_path.match(/\/$/)
  file = @bucket[file_path.gsub(/^\//,"")] rescue throw(:halt, [404, "Page not found\n"])

  content_type file.content_type
  file.value
end

get %r{.+} do で全てのgetメソッドリクエストに対してアプリケーションが対応する様にしています。

ここで、確かに見せるだけは問題無いのですが、少し大きな動画などのファイルを見せると、S3 Bucketの読み込みに時間がかかるらしくレスポンスがかなり遅くなります。S3のBucketをアメリカのサーバに移しても同じでした。

なのでキャッシングを有効にするためにRack Middlewareを使います。http://rtomayko.github.com/rack-cache/
私の場合は 'rack-cache'と'dalli'のgem をインストールしてconfig.ruに

require 'rack/cache'
require 'dalli'


use Rack::Cache,
  :verbose     => true,
  :metastore   => Dalli::Client.new,
  :entitystore => 'file:tmp/cache/rack/body'


を加えると読み込みが断然改善されました。でもこうするとHerokuサーバ上に大きなキャッシュのファイルを置くことになるので、直接/publicの下にファイルを置くのと大差ありませんね。実験としては面白いと思いますが、実用的ではないかもしれませんね。

また、Herokuのアプリケーションを動かしているサーバからのレスポンスはサーバがアメリカにあるためこれ以上改善できなさそうです。この点はHerokuが日本のサーバでサーバを提供してくれるのを期待するしかなさそうです。

No comments:

Post a Comment