Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

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つのヒントがあります。

  1. curlを含む応答ヘッダーのみを表示するには(たとえば、要求ヘッダーや応答本文を表示しない場合)、 -Iを使用します curl -I localhost:3000のようなオプション 。
  2. デフォルトでは、Railsは開発環境にキャッシュしません。 rails dev:cacheを実行する必要がある場合があります 最初に。
キャッシュコントロールHTTPヘッダー

キャッシングに関する限り、私たちが気にする主なヘッダーは 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を使用します。

ETags

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は、これらの問題のそれぞれを対象とする「鋭いナイフ」を提供します。必要なのは、それらをいつ使用するかを知ることだけです。


  1. Rubyを使用してコマンドラインアプリケーション(CLI)を構築する方法

    多くの人は、RubyがWebアプリケーションではないことを実行できることを忘れています。この記事では、それを改善するのに役立つコマンドラインアプリケーションを構築する方法を紹介したいと思います! 使い慣れているコマンドラインアプリケーションは次のとおりです。 psql rails bundler gem git コマンドラインアプリケーションを構築する方法はたくさんあります。この記事では、そのうちの3つに焦点を当てます。 あなたは学ぶつもりです : ARGVアレイ OptParseライブラリ トールの宝石 始めましょう! RubyARGV定数 コマンドラインア

  2. Ruby on Railsとは何ですか?なぜそれが役立つのですか?

    Ruby on Rails(RoRの場合もある)は、最も人気のあるオープンソースのWebアプリケーションフレームワークです。 Rubyプログラミング言語で構築されています。 Railsを使用すると、単純なものから複雑なものまで、アプリケーションの構築に役立ちます。Railsで実行できることには制限がありません。 フレームワークとは何ですか? フレームワークは、ソフトウェアを作成するときに使用する特定の構造を提供するコード、ツール、およびユーティリティのコレクションです。 この構造により、コードがより整理されます。 正しく使うことを学ぶと、作業が簡単になります。 レールは正確に何を