RubyonRailsアプリケーションでのHTTPキャッシング
キャッシュを説明する一般的な方法は、後ですばやく取得できるようにデータを保存することです。これは、再計算する必要がないように計算されたデータを保存することを意味する場合もありますが、データを再度フェッチする必要がないように、データをローカルに保存することを指す場合もあります。オペレーティングシステムは頻繁にアクセスされるデータをRAMに保持しようとするため、コンピュータはこれを常に実行し、ハードドライブやSSDからデータを再度フェッチする必要がないようにします。
同様に、ブラウザはダウンロード済みのリソースを再利用しようとします。初めて新しいウェブサイトにアクセスしたときに、おそらくこれを自分で見たことがあるでしょう。ブラウザがすべてをプルダウンする必要があるため、初期読み込みに時間がかかります。 すべての画像、JavaScript、スタイルシートを含めて必要です。面白い事実は、CNNホームページを新たにダウンロードすると、ブラウザが1993年頃の元のDoomゲームよりも多くのデータをフェッチすることです。好奇心旺盛なことに、このブログ投稿を書いている時点で、CNNは私のマシンに3MB強をダウンロードします。 〜15MBで、これは広告ブロッカーが有効になっている場合ですが、元のDoomインストーラーは〜2.2MBでした。
ブラウザがこのデータをキャッシュするには、サーバーとの調整が必要です。ブラウザは、何をどのくらいの期間キャッシュできるかを知る必要があります。そうしないと、サーバーに新しいバージョンが使用可能になったときに古いコンテンツが表示される可能性があります。この記事では、このクライアント/サーバーキャッシング調整がどのように実行されるか、およびRailsがそれを変更するために何を提供するかを見ていきます。
Ruby on Railsがこれをどのように処理するかに焦点が当てられていますが、実際のメカニズムはHTTP仕様の一部です。言い換えれば、ここで話しているキャッシングはインターネットのインフラストラクチャに組み込まれているため、最新のWebサイトとフレームワークの開発方法の基礎になります。 Rails、シングルページアプリケーション(SPA)、静的サイトなどのさまざまなフレームワークはすべて、これらのメカニズムを使用してパフォーマンスを向上させることができます。
HTTPリクエスト-レスポンス
あなたはおそらく、少なくとも大まかに言って、要求と応答のライフサイクルに精通しているでしょう。Webサイトのリンクをクリックすると、ブラウザがそのコンテンツの要求をサーバーに送信し、サーバーがそのコンテンツを送り返します(注ここでは、意図的に多くの複雑さを強調しています。
この前後のトランザクションで送信される実際のデータを少し掘り下げてみましょう。各HTTPメッセージには、ヘッダーと本文があります(
と混同しないでください)。 および HTMLタグ)。リクエストヘッダーは、アクセスしようとしているパスと使用するHTTPメソッド(GET / PUT / PATCH / POSTなど)をサーバーに通知します。必要に応じて、ブラウザの開発者ツールまたは
curl
などのコマンドラインツールを使用して、これらのヘッダーを詳しく調べることができます。 :
# curl -v honeybadger.io
...
> GET / HTTP/1.1
> Host: honeybadger.io
> User-Agent: curl/7.64.1
> Accept: */*
出力のこの最初の部分は、リクエストヘッダーです。 GET
を発行しています honeybadger.io
へ 。次に、サーバーが送り返したもの(「応答ヘッダー」)が続きます。
>
< HTTP/1.1 301 Moved Permanently
< Cache-Control: public, max-age=0, must-revalidate
< Content-Length: 39
< Content-Security-Policy: frame-ancestors 'none'
...
< Content-Type: text/plain
応答にはHTTPコードが含まれます(例: 200
成功または404
見つからないため)。この例では、curlが http
に接続しようとしているため、これは永続的なリダイレクト(301)です。 安全なhttps
にリダイレクトするURL URL。応答ヘッダーには、コンテンツタイプ( text / plain
)も含まれます。 ここにありますが、他のいくつかの一般的なオプションは text / html
です 、 text / css
、 text / javascript
、および application / json
。
応答本文はヘッダーの後に続きます。この場合、 301
であるため、本文は空白です。 リダイレクトには本文は必要ありません。 curl -v https://www.honeybadger.io
で再試行した場合 、ブラウザでソースを表示している場合と同じように、ここにホームページのコンテンツが表示されます。
これを自分で試してみたい場合は、次の2つのヒントがあります。
- curlを含む応答ヘッダーのみを表示するには(たとえば、要求ヘッダーや応答本文を表示しない場合)、
-I
を使用しますcurl -I localhost:3000
のようなオプション 。 - デフォルトでは、Railsは開発環境にキャッシュしません。
rails dev:cache
を実行する必要がある場合があります 最初に。
キャッシングに関する限り、私たちが気にする主なヘッダーは Cache-Control
です。 ヘッダ。これは、どのマシンがRailsサーバーからの応答をキャッシュできるか、およびそのキャッシュされたデータがいつ期限切れになるかを判断するのに役立ちます。 Cache-Control
内 ヘッダーにはいくつかのフィールドがあり、そのほとんどはオプションです。ここでは、最も関連性の高いエントリのいくつかを確認しますが、詳細については、w3.orgで公式のHTTP仕様を確認できます。
これは、すぐに使用できる基本的なRails応答ヘッダーのサンプルです。
< Content-Type: text/html; charset=utf-8
< Etag: W/"b41ce6c6d4bde17fd61a09e36b1e52ad"
< Cache-Control: max-age=0, private, must-revalidate
max-age
max-age
フィールドは、応答が有効な秒数を含む整数です。デフォルトでは、ビューのRails応答のこれは0に設定されます(つまり、応答はすぐに期限切れになり、ブラウザは常に新しいバージョンを取得する必要があります)。
public / private
public
を含む またはprivate
ヘッダーセットで、どのサーバーが応答をキャッシュできるか。ヘッダーにprivate
が含まれている場合 、コンテンツ配信ネットワーク(CDN)やプロキシなど、そこに到達するために通過した可能性のある他のサーバーではなく、要求元のクライアント(ブラウザなど)によってのみキャッシュされます。ヘッダーにpublic
が含まれている場合 、次に、これらの中間サーバーは応答をキャッシュできます。 Railsは各ヘッダーをprivate
に設定します デフォルトで。
must-revalidate
Railsはmust-revalidate
も設定します デフォルトではフィールド。つまり、クライアントはサーバーに接続して、キャッシュされたバージョンがまだ有効であることを確認してから使用する必要があります。キャッシュされたバージョンが有効かどうかを判断するために、クライアントとサーバーはETagを使用します。
ETagは、サーバーがクライアントに応答を送信するときにサーバーによって追加されるオプションのHTTPヘッダーです。通常、これは応答自体に対するある種のチェックサムです。クライアント(つまり、ブラウザ)がこのリソースを再度要求する必要がある場合、 If-None-Match
に受信したEtagが含まれます(以前の応答がキャッシュされていると想定)。 HTTPヘッダー。その後、サーバーは 304
で応答できます。 HTTPコード(「変更なし」)と空の本文。これは、サーバー上のバージョンが変更されていないことを意味するため、クライアントはキャッシュされたバージョンを使用する必要があります。
ETagには、強いものと弱いものの2種類があります(弱いタグは W /
で示されます) プレフィックス)。これらは同じように動作しますが、強力なETagは、リソースの2つのコピー(サーバー上のバージョンとローカルキャッシュ内のバージョン)が100%バイトごとに同一であることを意味します。ただし、弱いETagは、2つのコピーがバイトごとに同一ではない可能性があることを示していますが、キャッシュされたバージョンは引き続き使用できます。この一般的な例は、Railsの csrf_meta_tags
です。 ヘルパー。絶えず変化するトークンを作成します。したがって、アプリケーションに静的ページがある場合でも、Cross-Site-Request-Forgery(CSRF)トークンが原因で、更新されたときにバイトごとに同一になることはありません。 Railsはデフォルトで弱いETagを使用します。
RailsのETag
RailsはビューでETagを自動的に処理します。送信ヘッダーにETagが含まれ、受信ETagヘッダーをチェックして 304
を返すミドルウェアがあります。 (変更されていない)必要に応じてコード。ただし、特に、Railsはビューを動的に生成するため、そのビューのETagを把握する前に、すべてのレンダリング作業を実行する必要があります。つまり、ETagが一致していても、キャッシュされたバージョンがある場合にレンダリング手順を完全にスキップできるビューキャッシュのようなものとは対照的に、ネットワークを介してデータを送信するために必要な時間と帯域幅を節約するだけです。ただし、Railsには、生成されたETagを微調整する方法がいくつかあります。
ETagの変更から絶えず変化するCSRFトークンを克服する1つの方法は、 stale?
を使用することです。 ActionController
のヘルパー 。これにより、ETag(強いまたは弱い)を直接設定できます。ただし、ActiveRecordモデルなどのオブジェクトを渡すだけでも、オブジェクトの updated_at
に基づいてETagが計算されます。 タイムスタンプまたは最大のupdated_at
を使用します コレクションに合格した場合:
class UsersController < ApplicationController
def index
@users = User.includes(:posts).all
render :index if stale?(@users)
end
end
カールでページを押すと、結果を確認できます:
# curl -I localhost:3000 -- first page load
ETag: W/"af9ae8f2d66b9b6c4d0513f185638f1a"
# curl -I localhost:3000 -- reload (change due to CSRF token)
ETag: W/"f06158417f290334f47ea2124e08d89d"
-- Add stale? to controller code
# curl -I localhost:3000 -- reload
ETag: W/"04b9b99835c359f36551720d8e3ca6fe" -- now using `@users` to generate ETag
# curl -I localhost:3000 -- reload
ETag: W/"04b9b99835c359f36551720d8e3ca6fe" -- no change
これにより、クライアントがペイロード全体を再度ダウンロードする必要がある場合をより細かく制御できますが、それでもサーバーに毎回確認する必要があります。 キャッシュがまだ有効かどうかを判断します。そのチェックを完全にスキップしたい場合はどうなりますか?ここがmax-age
ヘッダーのフィールドが入ります。
expires_inおよびhttp_cache_forever
Railsは、 ActionController
にいくつかのヘルパーメソッドを提供します max-age
を調整します フィールド: Expires_in
およびhttp_cache_forever
。どちらも、名前に基づいて期待どおりに機能します:
class UsersController < ApplicationController
def index
@users = User.includes(:posts).all
expires_in 10.minutes
end
end
# curl -I localhost:3000
Cache-Control: max-age=600, private
Railsはmax-age
を設定しました 600(秒単位で10分)に変更し、 must-revalidate
を削除しました 分野。 private
を変更することもできます public:true
を渡すことでフィールド 名前付き引数。
http_cache_forever
ほとんどはexpires_in
の単なるラッパーです max-age
を設定します 100年になり、1ブロックかかります:
class UsersController < ApplicationController
def index
@users = User.includes(:posts).all
http_cache_forever(public: true) do
render :index
end
end
end
# curl -I localhost:3000
Cache-Control: max-age=3155695200, public
この種の非常に長期的なキャッシュが、Railsアセットに「フィンガープリント」が追加されている理由です。これは、ファイルのコンテンツのハッシュであり、 packs / js /application-4028feaf5babc1c1617b.js<などのファイル名を作成します。 / code> 。最後の「フィンガープリント」は、ファイルの内容をファイルの名前と効果的にリンクします。コンテンツが変更されると、ファイル名が変更されます。これは、ブラウザがこのファイルを永久に安全にキャッシュできることを意味します たとえそれが少しでも変化すると、指紋が変化するからです。ブラウザに関する限り、ダウンロードする必要があるのは完全に別のファイルです。
いくつかのキャッシュオプションについて説明したので、私のアドバイスは少し奇妙に思えるかもしれませんが、この記事のどの方法も使用しないようにすることをお勧めします! ETagとHTTPキャッシングについて知っておくとよいでしょう。また、Railsには、特定の問題に対処するための特定のツールがいくつか用意されています。ただし、注意点は、これは大きな問題ですが、このキャッシュはすべてアプリケーションの外部で行われるため、主に制御の範囲外にあるということです。このシリーズの前半で説明したように、Railsでビューキャッシングまたは低レベルキャッシングを使用していて、無効化の問題が発生した場合は、オプションがあります。 タッチコード>できます モデルを作成したり、更新されたコードをプッシュしたり、
Rails.cache
にアクセスしたりすることもできます 必要に応じてコンソールから直接実行しますが、HTTPキャッシングは使用しません。個人的には、 Rails.cache.clear
を実行する必要があります。 本番環境では、ユーザーがブラウザのキャッシュをクリアするまでサイトが壊れてしまうという問題に直面します(カスタマーサービスチームもあなたを気に入ってくれるでしょう)。
これで、Railsでのキャッシングに関するシリーズの終わりです。うまくいけば、それは有用で有益でした。キャッシングに関する私のアドバイスは、引き続き次のとおりです。できる限り少ないことを行いますが、必要なだけ多くのことを行います。パフォーマンスの問題が発生した場合は、頻繁にヒットするメソッドを探すことから始めます。おそらく、それらはメモ化することができます。リクエスト間で持続する値が必要ですか?たぶん、その頻繁に使用される方法は、いくつかの低レベルのキャッシングを使用する可能性があります。または、おそらく、それは特定の方法ではありません。ネストされたパーシャルをすべて処理しているだけで、処理速度が低下しています。この場合、ビューレベルのキャッシュが役立つ可能性があります。 Railsは、これらの問題のそれぞれを対象とする「鋭いナイフ」を提供します。必要なのは、それらをいつ使用するかを知ることだけです。
-
Rubyを使用してコマンドラインアプリケーション(CLI)を構築する方法
多くの人は、RubyがWebアプリケーションではないことを実行できることを忘れています。この記事では、それを改善するのに役立つコマンドラインアプリケーションを構築する方法を紹介したいと思います! 使い慣れているコマンドラインアプリケーションは次のとおりです。 psql rails bundler gem git コマンドラインアプリケーションを構築する方法はたくさんあります。この記事では、そのうちの3つに焦点を当てます。 あなたは学ぶつもりです : ARGVアレイ OptParseライブラリ トールの宝石 始めましょう! RubyARGV定数 コマンドラインア
-
Ruby on Railsとは何ですか?なぜそれが役立つのですか?
Ruby on Rails(RoRの場合もある)は、最も人気のあるオープンソースのWebアプリケーションフレームワークです。 Rubyプログラミング言語で構築されています。 Railsを使用すると、単純なものから複雑なものまで、アプリケーションの構築に役立ちます。Railsで実行できることには制限がありません。 フレームワークとは何ですか? フレームワークは、ソフトウェアを作成するときに使用する特定の構造を提供するコード、ツール、およびユーティリティのコレクションです。 この構造により、コードがより整理されます。 正しく使うことを学ぶと、作業が簡単になります。 レールは正確に何を