アプリケーションや Web サイトのパフォーマンスが成功の重要な要素であることは誰もが知っています。 ただし、アプリケーションや Web サイトのパフォーマンスを向上させるプロセスは必ずしも明確ではありません。 コードの品質とインフラストラクチャはもちろん重要ですが、多くの場合、いくつかの非常に基本的なアプリケーション配信テクニックに重点を置くことで、アプリケーションのエンドユーザー エクスペリエンスを大幅に改善できます。 そのような例の 1 つは、アプリケーション スタックにキャッシュを実装して最適化することです。 このブログ記事では、NGINX と NGINX Plus に含まれるコンテンツ キャッシュ機能を活用して、初心者と上級ユーザーの両方がパフォーマンスを向上させるのに役立つテクニックについて説明します。
コンテンツ キャッシュは、クライアントと「オリジン サーバー」の間に配置され、認識したすべてのコンテンツのコピーを保存します。 クライアントがキャッシュに保存されているコンテンツを要求した場合、オリジン サーバーに接続せずにコンテンツを直接返します。 これにより、コンテンツ キャッシュがクライアントに近くなるためパフォーマンスが向上し、毎回ページを最初から生成する作業を行う必要がなくなるため、アプリケーション サーバーがより効率的に使用されます。
Web ブラウザーとアプリケーション サーバーの間には、クライアントのブラウザー キャッシュ、中間キャッシュ、コンテンツ配信ネットワーク (CDN)、アプリケーション サーバーの前にあるロード バランサーまたはリバース プロキシなど、複数のキャッシュが存在する可能性があります。 リバース プロキシ/ロード バランサー レベルでもキャッシュを使用すると、パフォーマンスが大幅に向上します。
たとえば、昨年私は、読み込みが遅い Web サイトのパフォーマンスを調整するという作業を引き受けました。 最初に気付いたことの 1 つは、メインのホームページを生成するのに 1 秒以上かかるということでした。 デバッグを行った結果、ページがキャッシュ不可としてマークされていたため、各リクエストに応じて動的に生成されていたことがわかりました。 ページ自体はあまり頻繁に変更されず、パーソナライズもされていなかったため、これは必要ありませんでした。 実験として、ホームページをロードバランサーによって 5 秒間キャッシュするようにマークしたところ、それだけで目立った改善が見られました。 最初のバイトまでの時間は数ミリ秒に短縮され、ページの読み込みが目に見えて速くなりました。
NGINX は、通常、アプリケーション スタック内のリバース プロキシまたはロード バランサとして導入され、完全なキャッシュ機能セットを備えています。 次のセクションでは、NGINX を使用して基本的なキャッシュを構成する方法について説明します。
基本的なキャッシュを有効にするには、 proxy_cache_path
とproxy_cache の
2 つのディレクティブだけが必要です。 proxy_cache_path
ディレクティブはキャッシュのパスと構成を設定し、 proxy_cache
ディレクティブはそれをアクティブにします。
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off; server { # ... location / { proxy_cache my_cache; proxy_pass http://my_upstream; } }
proxy_cache_path
ディレクティブのパラメータは、次の設定を定義します。
levels は、
/path/to/cache/の下に 2 レベルのディレクトリ階層を設定します。 1 つのディレクトリに多数のファイルがあるとファイル アクセスが遅くなる可能性があるため、ほとんどの展開では 2 レベルのディレクトリ階層を推奨します。 levels
パラメータが含まれていない場合、NGINX はすべてのファイルを同じディレクトリに配置します。keys_zone は、
キャッシュ キーと使用タイマーなどのメタデータを格納するための共有メモリ ゾーンを設定します。 キーのコピーをメモリ内に保持することで、NGINX はディスクにアクセスすることなくリクエストがHIT
かMISS か
をすばやく判断できるため、チェックの速度が大幅に向上します。 1 MB のゾーンには約 8,000 個のキーのデータが保存できるため、例で構成された 10 MB のゾーンには約 80,000 個のキーのデータが保存できます。max_size は
キャッシュのサイズの上限を設定します (この例では 10 ギガバイト)。 これはオプションです。値を指定しないと、キャッシュが拡張され、使用可能なディスク領域がすべて使用されます。 キャッシュ サイズが制限に達すると、キャッシュ マネージャーと呼ばれるプロセスが最も最近使用されていないファイルを削除して、キャッシュ サイズを制限内に戻します。inactive は、
アイテムがアクセスされずにキャッシュ内に保持される期間を指定します。 この例では、60 分間要求されていないファイルは、有効期限が切れているかどうかに関係なく、キャッシュ マネージャー プロセスによってキャッシュから自動的に削除されます。 デフォルト値は10分( 10m
)です。 非アクティブなコンテンツは期限切れのコンテンツとは異なります。 NGINX は、キャッシュ制御ヘッダー(たとえば、 Cache-Control:max-age=120
) で定義されている期限切れのコンテンツを自動的に削除しません。 期限切れ(古い)コンテンツは、 inactive
で指定された時間アクセスされなかった場合にのみ削除されます。 期限切れのコンテンツにアクセスすると、NGINX はオリジン サーバーからそのコンテンツを更新し、非アクティブ
タイマーをリセットします。use_temp_path=off
ディレクティブは、それらのファイルをキャッシュされる同じディレクトリに書き込むように NGINX に指示します。 ファイルシステム間でのデータの不要なコピーを避けるため、このパラメータをオフ
に設定することをお勧めします。use_temp_pathは
、NGINX バージョン 1.7.10 およびNGINX Plus R6で導入されました。最後に、 proxy_cache
ディレクティブは、親ロケーション
ブロックの URL (例では/ ) に一致するすべてのコンテンツのキャッシュを有効にします。 また、 proxy_cache
ディレクティブをサーバー
ブロックに含めることもできます。これは、独自のproxy_cache
ディレクティブを持たないサーバーのすべてのロケーション
ブロックに適用されます。
NGINXコンテンツ キャッシュの強力な機能は、オリジン サーバーから最新のコンテンツを取得できない場合に、キャッシュから古いコンテンツを配信するように NGINX を構成できることです。 これは、キャッシュされたリソースのすべてのオリジン サーバーがダウンしているか、一時的にビジー状態の場合に発生する可能性があります。 NGINX は、エラーをクライアントに中継するのではなく、キャッシュからファイルの古いバージョンを配信します。 これにより、NGINX がプロキシするサーバーにさらなるレベルのフォールト トレランスが提供され、サーバー障害やトラフィックの急増が発生した場合でも稼働時間が確保されます。 この機能を有効にするには、 proxy_cache_use_stale
ディレクティブを追加します。
場所 / { # ... proxy_cache_use_staleエラー タイムアウト http_500 http_502 http_503 http_504; }
このサンプル構成では、NGINX がオリジン サーバーからエラー
、タイムアウト
、または指定された5xx
エラーのいずれかを受信し、要求されたファイルの古いバージョンがキャッシュ内にある場合、エラーをクライアントに中継するのではなく、古いファイルを配信します。
NGINX には、キャッシュのパフォーマンスを微調整するためのオプション設定が豊富に用意されています。 以下に、それらのいくつかをアクティブにする例を示します。
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off; server { # ... location / { proxy_cache my_cache; proxy_cache_revalidate on; proxy_cache_min_uses 3; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_background_update on; proxy_cache_lock on; proxy_pass http://my_upstream; } }
これらのディレクティブは、次の動作を構成します。
proxy_cache_revalidate は、
オリジン サーバーからコンテンツを更新するときに条件付きGET
リクエストを使用するように NGINX に指示します。 クライアントが、キャッシュ制御ヘッダーで定義されているようにキャッシュされているが期限切れになっているアイテムを要求した場合、NGINX は、オリジン サーバーに送信するGET
要求のヘッダーにIf-Modified-Since
フィールドを含めます。 これにより、NGINX が最初にファイルをキャッシュしたときにファイルに添付されたLast-Modified
ヘッダーに記録された時間以降に変更された場合にのみ、サーバーが完全なアイテムを送信するため、帯域幅が節約されます。proxy_cache_min_uses は、
NGINX がアイテムをキャッシュする前にクライアントがアイテムを要求する回数を設定します。 これは、最も頻繁にアクセスされるアイテムのみがキャッシュに追加されることを保証するため、キャッシュが常にいっぱいになっている場合に便利です。 デフォルトでは、 proxy_cache_min_uses
は 1 に設定されています。proxy_cache_use_stale
ディレクティブの更新
パラメータとproxy_cache_background_update
ディレクティブの有効化を組み合わせると、クライアントが期限切れまたはオリジン サーバーから更新中のアイテムを要求したときに、NGINX に古いコンテンツを配信するように指示します。 すべての更新はバックグラウンドで実行されます。 更新されたファイルが完全にダウンロードされるまで、すべてのリクエストに対して古いファイルが返されます。proxy_cache_lock
を有効にすると、複数のクライアントがキャッシュ内に現在存在しないファイル ( MISS
) を要求した場合、それらの要求のうち最初のものだけがオリジン サーバーに許可されます。 残りのリクエストは、そのリクエストが満たされるまで待機し、その後キャッシュからファイルを取得します。 proxy_cache_lock
が有効になっていない場合、キャッシュ ミスとなるすべてのリクエストは直接元のサーバーに送信されます。複数のハードドライブがある場合は、NGINX を使用してキャッシュをそれらの間で分割できます。 以下は、リクエスト URI に基づいてクライアントを 2 つのハード ドライブに均等に分割する例です。
proxy_cache_path /path/to/hdd1 levels=1:2 keys_zone=my_cache_hdd1:10m max_size=10g inactive=60m use_temp_path=off;
proxy_cache_path /path/to/hdd2 levels=1:2 keys_zone=my_cache_hdd2:10m
max_size=10g inactive=60m use_temp_path=off;
split_clients $request_uri $my_cache {
50% “my_cache_hdd1”;
50% “my_cache_hdd2”;
}
server {
# ...
location / {
proxy_cache $my_cache;
proxy_pass http://my_upstream;
}
}
2 つのproxy_cache_path
ディレクティブは、2 つの異なるハード ドライブ上の 2 つのキャッシュ ( my_cache_hdd1
とmy_cache_hdd2
) を定義します。 split_clients
構成ブロックは、リクエストの半分 ( 50%
) の結果をmy_cache_hdd1
にキャッシュし、残りの半分をmy_cache_hdd2
にキャッシュすることを指定します。 $request_uri
変数 (リクエスト URI) に基づくハッシュによって、各リクエストに使用されるキャッシュが決定され、その結果、特定の URI に対するリクエストは常に同じキャッシュにキャッシュされます。
このアプローチは RAID ハード ドライブ セットアップの代替ではないことに注意してください。 ハード ドライブに障害が発生すると、障害が発生したハード ドライブに送信された要求に対して 500 応答コードがユーザーに表示されるなど、システムで予期しない動作が発生する可能性があります。 適切な RAID ハード ドライブ設定により、ハード ドライブの障害に対処できます。
このセクションでは、NGINX コンテンツ キャッシュに関するよくある質問に回答します。
はい、 add_header
ディレクティブを使用すれば可能です:
add_header X-Cache-Status $upstream_cache_status;
この例では、クライアントへの応答にX-Cache-Status
HTTP ヘッダーを追加します。 $upstream_cache_status
に指定できる値は次のとおりです。
MISS
– 応答がキャッシュ内に見つからなかったため、元のサーバーから取得されました。 応答はキャッシュされている可能性があります。BYPASS
– リクエストがproxy_cache_bypass
ディレクティブに一致したため、応答はキャッシュから提供されるのではなく、オリジン サーバーから取得されました (以下の「キャッシュに穴を開けることはできますか? 」を参照してください)。 応答はキャッシュされている可能性があります。EXPIRED
– キャッシュ内のエントリの有効期限が切れています。 応答には、元のサーバーからの最新のコンテンツが含まれます。STALE
– オリジン サーバーが正しく応答せず、 proxy_cache_use_stale
が構成されているため、コンテンツは古くなっています。更新中
– エントリは以前のリクエストに応じて現在更新中であり、 proxy_cache_use_stale 更新
が設定されているため、コンテンツは古くなっています。REVALIDATED
– proxy_cache_revalidate
ディレクティブが有効になり、NGINX は現在キャッシュされたコンテンツがまだ有効であることを確認しました ( If-Modified-Since
またはIf-None-Match
)。HIT
– 応答には、キャッシュから直接取得した有効な最新のコンテンツが含まれます。NGINX は、オリジン サーバーに将来の日付と時刻を含むExpires
ヘッダー、またはmax-age
ディレクティブがゼロ以外の値に設定されたCache-Control
ヘッダーが含まれている場合にのみ、応答をキャッシュします。
デフォルトでは、NGINX はCache-Control
ヘッダー内の他のディレクティブを尊重します。つまり、ヘッダーにPrivate
、 No-Cache
、またはNo-Store
ディレクティブが含まれている場合は、応答をキャッシュしません。 また、 Set-Cookie
ヘッダーを含む応答もキャッシュされません。 さらに、 GET
およびHEAD
リクエストへの応答のみをキャッシュします。 以下の回答で説明されているように、これらのデフォルトを上書きできます。
proxy_buffering
がoff
に設定されている場合、NGINX は応答をキャッシュしません。 デフォルトではオンに
なっています。
Cache-Control
ヘッダーを無視できますか?はい、 proxy_ignore_headers
ディレクティブを使用します。 たとえば、次の構成の場合:
場所 /images/ { proxy_cache my_cache; proxy_ignore_headers Cache-Control; proxy_cache_valid any 30m; # ... }
NGINX は、 /images/の下にあるすべてのもののCache-Control
ヘッダーを無視します。 proxy_cache_valid
ディレクティブは、キャッシュされたデータの有効期限を強制し、 Cache-Control
ヘッダーを無視する場合に必要です。 NGINX は有効期限のないファイルをキャッシュしません。
Set-Cookie
を含むコンテンツをキャッシュできますか?はい、前の回答で説明したように、 proxy_ignore_headers
ディレクティブを使用します。
POST
リクエストをキャッシュできますか?はい、 proxy_cache_methods
ディレクティブを使用すると可能です:
proxy_cache_methods GET HEAD POST;
この例では、 POST
リクエストのキャッシュを有効にします。
はい、 Cache-Control
ヘッダーで許可されている場合は可能です。 動的コンテンツを短期間でもキャッシュすると、元のサーバーとデータベースの負荷が軽減され、リクエストごとにページを再生成する必要がなくなるため、最初のバイトまでの時間が短縮されます。
はい、 proxy_cache_bypass
ディレクティブを使用すれば可能です:
場所 / { proxy_cache_bypass $cookie_nocache $arg_nocache; # ... }
このディレクティブは、NGINX が最初にキャッシュ内でコンテンツを検索するのではなく、すぐにオリジン サーバーからコンテンツを要求するリクエスト タイプを定義します。 これは、キャッシュに「穴を開ける」と呼ばれることもあります。 この例では、NGINX は、 nocache
クッキーまたは引数を持つリクエストに対してこれを実行します (例: http://www.example.com/?nocache=true )
。 NGINX は、バイパスされない将来のリクエストに対して結果の応答をキャッシュすることができます。
NGINX が生成するキーのデフォルトの形式は、次のNGINX 変数の MD5 ハッシュに似ています: $scheme$proxy_host$request_uri
。実際に使用されるアルゴリズムは少し複雑です。
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
server {
# ...
location / {
proxy_cache my_cache;
proxy_pass http://my_upstream;
}
}
このサンプル構成では、 http://www.example.org/my_image.jpg
のキャッシュ キーはmd5(“http://my_upstream:80/my_image.jpg”)
として計算されます。
ハッシュ値では、実際のホスト名 ( www.example.com
) ではなく、 $proxy_host
変数が使用されることに注意してください。 $proxy_host は
、 proxy_pass
ディレクティブで指定されたプロキシ サーバーの名前とポートとして定義されます。
キーの基礎として使用される変数 (またはその他の用語) を変更するには、 proxy_cache_key
ディレクティブを使用します (次の質問も参照)。
はい、キャッシュ キーは任意の値に設定できます。たとえば、次のようになります。
proxy_cache_key $proxy_host$request_uri$cookie_jessionid;
この例では、 JSESSIONID
Cookie の値をキャッシュ キーに組み込みます。 URI が同じだがJSESSIONID
値が異なるアイテムは、一意のアイテムとして個別にキャッシュされます。
ETag
ヘッダーを使用しますか?NGINX 1.7.3 およびNGINX Plus R5以降では、 ETag
ヘッダーはIf-None-Match
とともに完全にサポートされています。
ファイルがキャッシュ内で最新である場合、NGINX はバイト範囲要求を尊重し、アイテムの指定されたバイトのみをクライアントに提供します。 ファイルがキャッシュされていない場合、または古い場合、NGINX はオリジン サーバーからファイル全体をダウンロードします。 リクエストが 1 バイトの範囲に対するものである場合、NGINX はダウンロード ストリームでその範囲が検出されるとすぐにその範囲をクライアントに送信します。 リクエストで同じファイル内の複数のバイト範囲が指定されている場合、ダウンロードが完了すると、NGINX はファイル全体をクライアントに配信します。
ダウンロードが完了すると、NGINX はリソース全体をキャッシュに移動し、単一の範囲または複数の範囲を問わず、今後のすべてのバイト範囲リクエストがキャッシュからすぐに満たされるようにします。
NGINX がアップ
ストリーム サーバーへのバイト範囲リクエストを尊重するには、アップストリーム
サーバーがバイト範囲リクエストをサポートしている必要があることに注意してください。
NGINX Plus は、キャッシュされたファイルの選択的な消去をサポートしています。 これは、ファイルがオリジン サーバー上で更新されているが、NGINX Plus キャッシュ内ではまだ有効な場合 ( Cache-Control:max-age
がまだ有効であり、 proxy_cache_path
ディレクティブのinactive
パラメータによって設定されたタイムアウトが期限切れになっていない場合) に役立ちます。 NGINX Plus のキャッシュ消去機能を使用すると、このファイルを簡単に削除できます。 詳細については、 「キャッシュからのコンテンツの消去」を参照してください。
プラグマ
ヘッダーをどのように処理しますか?Pragma:no-cache
ヘッダーは、すべての中間キャッシュをバイパスし、要求されたコンテンツの元のサーバーに直接アクセスするためにクライアントによって追加されます。 NGINX はデフォルトではPragma
ヘッダーを尊重しませんが、次のproxy_cache_bypass
ディレクティブを使用してこの機能を設定できます。
場所 /images/ { proxy_cache my_cache; proxy_cache_bypass $http_pragma; # ... }
Cache-Control
ヘッダーのstale-while-revalidate
およびstale-if-error
拡張をサポートしていますか?はい、 NGINX Plus R12および NGINX 1.11.10 以降では可能です。 これらの拡張機能の機能:
Cache-Control
HTTP ヘッダーのstale-while-revalidate
拡張により、キャッシュされた応答が現在更新中の場合、古い応答を使用することが許可されます。Cache-Control
HTTP ヘッダーのstale-if-error
拡張により、エラーが発生した場合に古いキャッシュされた応答を使用することが許可されます。これらのヘッダーは、上記のproxy_cache_use_stale
ディレクティブよりも優先度が低くなります。
Vary
ヘッダーをサポートしていますか?はい、 NGINX Plus R5および NGINX 1.7.7 以降では可能です。 Vary
ヘッダーの概要は次のとおりです。
NGINX キャッシュをカスタマイズおよび調整する方法は他にもたくさんあります。 NGINX によるキャッシュについてさらに詳しく知りたい場合は、次のリソースをご覧ください。
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"