Feb 16, 2007

もう少しだけmod_cacheを深追いしてみる

前のエントリーで書いた方法だとブラウザによってはページをリロードするたびに待たされることになります。

Ogawa::Buzz: mt-search.cgiを mod_cacheで超高速化する!!

なんでかな?と思ったので、動的コンテンツ(この場合はURLにクエリ文字列を含む動的コンテンツ)へのリクエストに対する、(mod_cacheで実現された)サーバーサイドキャッシュの振る舞いをもう少し深追いしてみました。

サーバーサイドキャッシュがない場合

サーバーサイドキャッシュがない場合には、すべての動的コンテンツへのリクエストごとにレンダリングを行い、そのデータがレスポンスとして返されます。

動的コンテンツで、Last-Modified, ETagヘッダを含むレスポンスを行い、If-Modified-Since, If-None-Matchヘッダを含むリクエストに対して304 Not Modifiedを返すことでレンダリング処理やトラフィックを削減することもできます。できますが、先のエントリーで見たようにCGIのプロセス起動のオーバーヘッドが数100ミリ秒もかかってしまう場合にはほとんど意味がありません。FastCGIやモジュール版PHPなどの場合には効果が期待できます。

サーバーサイドキャッシュがある場合

先のエントリーで示したように、Expiresヘッダをレスポンスとして返す動的コンテンツは、サーバーサイドキャッシュによってキャッシュされます。

下図に示すように、ブラウザがアクセスした際にそのURLがキャッシュされていれば、その内容をそのままレスポンスとして返します。また、キャッシュされていない場合やそのキャッシュが有効期限を過ぎている場合にはバックエンドの動的コンテンツにアクセスし、レンダリング結果を得、それをキャッシュした上でブラウザに返します。

一方、ブラウザのリロードボタンを押した場合など、ブラウザからのリクエストがキャッシュ制御情報を示すヘッダ(例えば、Cache-Control: no-cache、Cache-Control: max-age=0、Pragma: no-cacheなど)を含む場合には、下図のように、mod_cacheはキャッシュのチェックを行わず、バックエンドシステムにアクセスし、レンダリング結果を得、それをキャッシュした上でブラウザに返します。

ただし、mod_cacheにはCacheIgnoreCacheControlというディレクティブが用意されており、値をOnにした場合にはリクエストに含まれるCache-Controlヘッダを無視して、キャッシュにヒットすればキャッシュ内の情報を返します。これを設定すると期限切れになるまでキャッシュが更新されなくなるため効率は良いですが、有効期限を長く設定しているとキャッシュデータと真のデータがinconsistentな状態が長く続いてしまいます。この点に関しては下で再度触れます。

サーバーサイドキャッシュとConditional GETの組み合わせ

Expiresヘッダに加え、Last-Modified、ETagヘッダの一方または両方をレスポンスとして返す動的コンテンツもまた、上記と同様にサーバーサイドキャッシュによってキャッシュされます。

ただし、ブラウザはLast-Modified、ETagヘッダを含むURLへ再度アクセスする場合には、If-Modified-Since, If-None-Matchヘッダ付きのリクエスト(Conditional GET)を行う場合があります。この場合には、キャッシュ上にあるレスポンスヘッダと対応が取れていれば304 Not Modifiedを返し、対応が取れていなければIf-Modified-Since, If-None-Matchヘッダなしのリクエストと同等に扱います。つまり、キャッシュが無効でなければキャッシュデータを返し、無効ならバックエンドに問い合わせます。

ページロード時のブラウザの振る舞いとCacheIgnoreCacheControlの設定

Last-Modified, ETagヘッダの付いた(より正確にはブラウザはローカルにキャッシュできる)ページのロード時にどういうリクエストヘッダを送るかはブラウザによって異なります。Firefox 1.5/2.0、IE7で調べてみたらこんな感じ(下表中、Firefoxの強制リロードはCtrl+Reload、IE7の強制リロードはShift+Reloadを行ったものです)。

ヘッダ If-Modified-Since If-None-Match Cache-Control Pragma
Firefox 通常ロード
Firefox リロード max-age=0
Firefox 強制リロード no-cache no-cache
IE7 通常ロード
IE7 リロード
IE7 強制リロード no-cache no-cache

つまり、CacheIgnoreCacheControlをOff(デフォルト)にしている場合には、通常のリロード操作を行ったときIE7では304 Not Modifiedが返りますが、Firefox 1.5/2.0ではバックエンドへの問い合わせが発生します。リロード操作というのはWebブラウザでは案外普通にやってしまう操作なのでこの違いは大きいです。一方でCacheIgnoreCacheControlをOnにしている場合には、IE7でもFirefoxでも304 Not Modifiedが返ります。

ここで2つの戦略があり得ます。つまり、

  • CacheIgnoreCacheControlはOffにする
  • Firefoxユーザが不幸になることは看過する
  • ただし、Expiresはキャッシュの量が限度に達するまでの任意の期間を指定できる

という戦略と、

  • CacheIgnoreCacheControlはOnにする
  • Firefoxユーザも幸せ
  • ただし、ブラウザからサーバーサイドキャッシュを無効化する手立てがないので、Expiresに指定する時間も応分に短めに設定する

という戦略です。このサイトのタグアーカイブでは試しに下の戦略を採用してみています。

mt-search.cgiでLast-Modified, ETagヘッダを返すようにするパッチ

先のエントリーでは、mt-search.cgiのレスポンスにExpiresヘッダを追加するパッチを示しましたが、ここではLast-Modified, ETagヘッダも追加するためのパッチを示しておきます。

MT-3.34-MT-App-Search.patch

このパッチを当てれば、先のエントリーのBootstrap.pmへの修正は必要ありません。

About Me

My Photo

つくばで働く研究者

Total Pageviews

Amazon

Copyright 2012 Ogawa::Buzz | Powered by Blogger
Design by Web2feel | Blogger Template by NewBloggerThemes.com