2009年4月19日日曜日

mod_cacheまわり

Apache Web Serverでコンテンツのcacheをしたくなり調査しました。
まず、cache方法には3種類あって

  1. Reverse Proxy
    Web serverとWeb browserとの間に介入して、cacheする。Apacheにもその機能はあるし、別のツールでも実現できる。Web serverとは別モノを必要とするので構造がややこしくなるのでパス。
  2. mod_file_cache
    Apacheが最終的に読み込むfileそのものをcacheしてしまう。方法をfile openしっぱなしやmmap()しっぱなしなど、安直だけどうまくいけば効果的。cacheの対象がfileなので動的なコンテンツには対応できない。パス。
  3. mod_cache
    Apacheのfilterとして動作し、出力内容をcacheし、次に同じリクエストがあればcache内容を返す。動的なコンテンツにも対応する。本命。
というわけでmod_cacheの設定をします。
そもそもmod_rewriteを使った記事を参考に、コンテンツ圧縮をしていました。というのもApacheのmod_deflateはリクエストの度にコンテンツ圧縮をするので効率が悪かったのです。mod_cacheで圧縮後のコンテンツをcacheできればmod_rewriteのトリッキーな方法ともおさらばです。

さてmod_cacheにはdisk cacheを行うmod_disk_cacheとmemory cacheを行うmod_mem_cacheがあります。ディスクアクセスを減らしたいのでここはmod_mem_cacheで決まりです。
mod_mem_cacheはプロセスごとにmemory cacheを行うためマルチプロセスとなるpreforkモデルでは効果がありません。ここはマルチスレッドを併用するworkerモデルに切り替えます。(Debianなのでapache2-mpm-workerパッケージをインストールするだけ。)

次にmod_deflateによるコンテンツ圧縮の設定です。
AddOutputFilterByType DEFLATE text/plain text/xml application/javascript
text/htmlは加えません。そもそも細かい設定もしません。そこには事情があります。
text/htmlをコンテンツ圧縮したところIE6が固まりました。正確にはKeep Aliveが切断されるまで処理が固まり、他のコンテンツはタイムアウトエラーになりました。mod_setenvifを使用してIE6のみKeep Aliveを拒否してみましたがいまいち効果もなく、またConnection continueが使えないのは応答速度にも影響します。htmlコンテンツは大きくもなく、数も少ないため圧縮対象外としました。
mod_deflateに対して細かい設定をしないことにも事情があります。コンテンツ圧縮を行うことで古いブラウザでは問題を引き起こします。一般的にはそこでブラウザごとに圧縮可否を変えます。これを行ってしまうと、Varyレスポンスヘッダに影響を与えてしまいます。つまり、User-Agentリクエストヘッダに応じてコンテンツを切り替えていることになり「Vary: Accept-Encoding, User-Agent」となってしまいます。mod_cacheはこれを見てUser-Agentごとに異なるcacheを生成します。User-Agentにはブラウザバージョンなどの文字列が含まれているためほとんどcache hitが見込めなくなります。というわけであえてUser-Agentには依存しない設定にしました。

Vary: Accept-Encodingにも問題がありました。リクエストヘッダに「Accept-Encoding: gzip」とあれば圧縮する(なければ圧縮しない)わけですから、Varyに含めるのは当然なのですが、Accept-Encodingの内容がブラウザごとに異なるため、それぞれにcacheを生成してしまいます。そこで考えました。
SetEnvIf Accept-Encoding "\bgzip\b" gzip
RequestHeader set Accept-Encoding gzip env=gzip
RequestHeader unset Accept-Encoding env=!gzip
Accept-Encodingにgzipを含むか含まないかの2択でヘッダを書き換えてしまう方法です。これはどうやらダメでした。cacheのhit判定にはこの書き換えは含まれず、逆にcache保存時にはこの書き換えの影響を受けるためcache hitしなくなりました。というわけでこれに関しては目をつぶります。

他にも問題がありました。mod_deflateはコンテンツ圧縮後のETagに「-gzip」を付与します。cacheのhit判定には「-gzip」が付与されていないためこれまたcache hitしませんでした。そこで
FileETag None
でETagを消しました。元々Last-Modifiedレスポンスヘッダは付いているのでそれほど問題にはなりません。

最後にmod_cacheとmod_mem_cacheの設定です。これはパラメータを適切に設定するだけです。