Skip to content

rack cache memcached static assets rails31

iwhurtafly edited this page Jun 16, 2012 · 7 revisions

少し複雑になりますけど、静的アセットを供給するためには、CDNを使用することが最も効率的なオプションとなります。 詳細はこちらの記事を参照して下さい。 CDN asset host

Ruby on Railsのアプリケーションでは、Cedar stack上にアセットを効率的に供給するために Rack::Cacheを使うべきです。 適切なRack::Cacheの使用は、レスポンスタイムを改善し、ロードを減らし、アプリケーションを通して静的アセットを 供給する際に重要となります。

この記事は、Rack::Cacheを使ったキャッシュのアセットの概念についてサマライズしています。 また、Rails 3.1のアプリケーションとアセットパイプラインの適切な設定に関しても、手順を示します。

この記事で参照するアプリケーションのサンプルコードは、 GitHub上に置かれており、 http://rack-cache-demo.herokuapp.com/で 動作を確認することが出来ます。

Rackを理解する

Rack::Cacheは、Rackのミドルウェアで、アプリケーションに対し、HTTPのキャッシュを 可能とし、メインのRailsアプリケーションからワークを獲得すること無しに、バックエンドのストレージからアセットを供給することを アプリケーションに対し許可します。

ミドルウェアと呼ばれる、リクエストを得て、それをステップの連続でパスすることにより、Rackは高いレベルで動作します。 それぞれのRackミドルウェアは、いくつかのアクションを実行し、それからRack内の次のミドルウェアへリクエストをパスします。 最終的にはリクエストは適切にフォーマットされ、アプリケーションに返されます。

Rackは、軽量かつ柔軟に記述されます。もしミドルウェアが、直接、リクエストに対しレスポンス出来るのであれば、 Railsアプリケーションへアクセスする必要はありません。これは、リクエストがより早く返され、Railsアプリケーションの 全体のロードを減らすことを意味します。

Rack::Cache のストレージ

Rack::Cacheは、2つの異なるストレージ領域を持っています。: metaとentityのストアです。 metaストアは、HTTPリクエストとレスポンスヘッダーを含む、それぞれのキャッシュに関する高度なレベルの情報を格納します。 この領域は、高頻度でアクセスされる小さな塊のデータを格納します。 entityストアは、metaストアよりはアクセス頻度が下がるものの、比較的大きめのデータ量となるbodyの中身を格納します。

Rack::Cacheは、3つの異なるストレージエンジンを提供します。: fileheapmemcacheです。 fileエンジンの格納データは、低速なものの、メモリ効率は良いです。 heapエンジンを使うことは、プロセスメモリをより高速に使えるものの、無制限にプロセスメモリが増え続けるのであれば、 パフォーマンスに大きな影響を与えます。 memcacheエンジンを使うことは、多数のオブジェクトを格納するには適さないものの、最も高速な手段となります。

entityストアとmetaストアに関する情報は、こちらを参照して下さい。 Rack Cache Storage

memcacheエンジンでmetaストアを使うことは、共有されるmetaデータへ高速なアクセスが可能となることを意味します。 fileエンジンでentityストアとより多くのオブジェクトを扱うという選択肢もあるものの、 効率的で予測可能なパフォーマンスを発揮し、Herokuでは推奨されています。

Memcacheをローカルへインストール

その他OSへのインストール方法は、こちらで見つけることが出来ます。 Memcache add-on article

アプリケーションをローカルで実行し、Rack::Cacheをテストするために、Memcacheをインストールする必要があります。 Mac OSXでは、homebrewのようなツールを使用することで、インストール可能です。

:::term
$ brew install memcached

homebrewでのインストールの最後に、システム起動時にMemcacheをマニュアルでスタートさせるか、自動でスタートさせるかの インストラクションが表示されます。

Railsのキャッシュストアの設定

Herokuでは、Memcacheと一緒に、Rack::Cacheのバックエンドとして Dalliというgemを使用することを推奨しています。 Gemfileへ下記を追加して下さい。:

:::ruby
gem 'dalli'

Dalliのアプリケーション依存関係を確立するためにbundle installを実行した後、 config/application.rb内の下記を修正することで、RailsへDalliをキャッシュストアとして使うことを伝えます。

:::ruby
config.cache_store = :dalli_store

ローカルのRailsコンソールを起動し、シンプルなKey-Valueのセット/ゲットをすることで、 Dalli/Memcacheの設定を確定して下さい。

:::term
$ rails c
> memcache = Dalli::Client.new
> memcache.set('foo', 'bar')
> memcache.get('foo')
'bar'

Memcacheを使うためのアプリケーション設定が完了しますと、次は、Rack::Cacheを設定することとなります。

Rack::Cacheの設定

ビルトインされたRailsのRack::Cacheへ、適切なストレージのバックエンドを明示するため、 config/environments/production.rbの環境設定ファイルを修正して下さい。

:::ruby
config.action_dispatch.rack_cache = {
  :metastore    => Dalli::Client.new,
  :entitystore  => 'file:tmp/cache/rack/body',
  :allow_reload => false
}

仮に明示しなかった場合、`Dalli::Client.new`は自動的に`MEMCACHE_SERVERS`環境変数からMemcacheサーバの場所を 検索します。環境変数から取得出来なければ、localhostと初期ポートをデフォルトとします。

静的アセットの供給

`production.rb`のコンフィグに関しては、GitHub上のものを参照して下さい。 reference application

アプリケーションが適切に、静的アセットを供給、無効、リフレッシュ出来るように、config/environments/production.rb内の いくつかのコンフィグを更新する必要があります。 serve_static_assetsの設定を行うことで、Railsが静的アセットを供給出来るよう許可して下さい。

:::ruby
config.serve_static_assets = true

さらに、Cache-Controlヘッダーを設定することで、アイテムがどのくらいの期間キャッシュされるかを明示して下さい。 Cache-Controlヘッダーの設定無しに、静的ファイルがRack::Cacheによりストアされることはありません。

:::ruby
config.static_cache_control = "public, max-age=2592000"

これらの設定は、Rack::Cacheがとても長い時間、静的な要素をストアすることを意味します。 変更されたファイルのキャッシュを適切に無効化するために、Railsはファイル名のハッシュダイジェストを更新します。 config.assets.digestを設定することで、この動作を可能とします。

:::ruby
config.assets.digest = true

本番環境でもこのキャッシュ設定を有効としたいでしょう。下記の設定を行って下さい。

:::ruby
config.action_controller.perform_caching = true

Memcacheアドオンの準備

Rack::Cacheのmetaストアとして、Memcacheを使うでしょうから、 Memcacheアドオン をHeroku上のアプリケーションへ追加する必要があります。

:::term
$ heroku addons:add memcache
----> Adding memcache to myapp... done, v25 (free)

本番環境のキャッシュ

アプリケーションをHerokuへディプロイし、キャッシュの結果を見るために、heroku logsコマンドを実行して下さい。

:::term
$ git push heroku master
$ heroku logs --ps web -t
ハードリフレッシュは、ブラウザのキャッシュをクリアし、アセットのリクエストを強制するために役立ちます。

沢山あるログの中からcacheエントリを参照して下さい。 miss, storeは、対象のアイテムがキャッシュ内に見つからなかったものの、次回リクエスト時のために保存されたことを示唆しています。

:::term
cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss, store
cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss, store
cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss, store

freshは、対象のアイテムがキャッシュ内に見つかり、キャッシュから供給されることを示唆しています。

:::term
cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] fresh
cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] fresh
cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] fresh

これで、あなたのRails 3.1+のアプリケーションが、Memcacheを使い、ダイナミックなアプリケーションのリクエストを 実現させるためにdynosを有効活用し、静的アセットをキャッシュするよう設定されたこととなります。

デバッグ

もし設定が適切になされていないと、storefreshの代わりに、missを見ることになります。

:::term
cache: [GET /assets/application-95bd4fe1de99c1cd91ec8e6f348a44bd.css] miss
cache: [GET /assets/application-95fca227f3857c8ac9e7ba4ffed80386.js] miss
cache: [GET /assets/rails-782b548cc1ba7f898cdad2d9eb8420d2.png] miss

この現象が発生した場合、アセットのレスポンスヘッダーを検査するために、curlを使い、 Cache-Controlヘッダーが存在していることを確実にします。

:::term
$ curl -I 'http://rack-cache-demo.herokuapp.com/assets/shipit-72351bb81da0eca408d9bd8342f1b972.jpg'
HTTP/1.1 200 OK
Age: 632
Cache-Control: public, max-age=2592000
Content-length: 70522
Etag: "72351bb81da0eca408d9bd8342f1b972"
Last-Modified: Sun, 25 Mar 2012 01:51:21 GMT
X-Rack-Cache: fresh

レスポンスヘッダーは、Cache-Controlを含んでいるべきであり、config.static_cache_control内に設定した 詳細な値(例えば、public, max-age=2592000)を返すべきです。 また、アセットの状態(fresh/store/miss)を示すX-Rack-Cacheヘッダーが表示されているかを確認して下さい。 もし、予期せぬ結果が表示されているのであれば、本番環境の設定を確認して下さい。

thin上のRackサーバー環境

thinをwebサーバーとして使用する場合、Rack::Cacheの設定が適切になされているかを Procfile内の開始位置に$RACK_ENVを渡すことで確認して下さい。

web: bundle exec rails server thin -p $PORT -e $RACK_ENV

ファイルバージョンの不一致

もし、ファイルを変更したにも関わらず、サーバーが古いファイルを返し続ける場合、ディプロイする前に、 Gitのリポジトリにコミットされているかを確認して下さい。 ファイルがコンパイル済みのコードに存在するかは、heroku run bashコマンドを実行し、 public/assetsディレクトリの中身を見ることで確認可能です。 このディレクトリは、ハッシュ化されたファイル名のアセットを管理しています。

:::term
$ heroku run bash
Running bash attached to terminal... up, run.1
$ ls public/assets
application-95bd4fe1de99c1cd91ec8e6f348a44bd.css      application.css           manifest.yml
application-95bd4fe1de99c1cd91ec8e6f348a44bd.css.gz   application.css.gz        rails-782b548cc1ba7f898cdad2d9eb8420d2.png
application-95fca227f3857c8ac9e7ba4ffed80386.js       application.js            rails.png
application-95fca227f3857c8ac9e7ba4ffed80386.js.gz    application.js.gz

また、ファイルがRailsのmanifest.ymlにリストされていることを確認して下さい。

:::term
$ cat public/assets/manifest.yml
rails.png: rails-782b548cc1ba7f898cdad2d9eb8420d2.png
application.js: application-95fca227f3857c8ac9e7ba4ffed80386.js
application.css: application-95bd4fe1de99c1cd91ec8e6f348a44bd.css

もし、探しているファイルが見つからないのであれば、ローカルでbundle exec rake assets:precompile RAILS_ENV=productionを 実行し、public/assetsディレクトリにファイルが存在していることを確認して下さい。

Clone this wiki locally