emahiro/b.log

Drastically Repeat Yourself !!!!

Lerning about Cache part1

仕事で真剣にCache戦略を考える機会があったので、これまでなんとなくしか理解してこなかったブラウザキャッシュについて調べてみました。
今回はpart1です。

参考

Real World HTTP
HTTP キャッシュ - HTTP | MDN
HTTP キャッシュ  |  Web  |  Google Developers

summary

  • キャッシュとは?
  • 更新日時でCacheする
  • etagでキャッシュする

Cacheとは?

本エントリではブラウザCacheに絞って調べました。

そもそもCacheとはウェブサイトのリッチ化の流れの中で

すでにダウンロード済みで、内容に変化がなければダウンロードを抑制し、それによってパフォーマンスを上げるメカニズム (Real World HTTP P.48)

とされています。
コンテンツの差分がなければそもそもサーバーにアクセスせずとも、手元にキープしておいたコンテンツを使うメカニズムのことで、サーバーアクセスを減らし、応答速度を高速化させてるように見せることができます。

EndUserにとっては見るコンテンツが変わらなければ、裏でサーバー通信をしていようが、手元にためてたコンテンツを表示させようが大差はありません。

通常

f:id:ema_hiro:20180326022046p:plain

Cacheあり

f:id:ema_hiro:20180326022054p:plain

ざっくり簡単に書くとこんな感じです。

リソースが最新かどうかを確認する

Cacheする以前に、リソースの更新の有無を確認する方法をまずは調べます。
というのも、あるリソースを取得するときに、200OKでレスポンスを受け取った場合、bodyに取得したリソース(jsonなどの形式)が入ってきます。
リソースも情報を含んでいるので、データのサイズに応じて通信はかかるし、取得するまで時間がかかってしまいます。このリソースが変わらないのに、毎回通信が発生したり、そもそもリソースの情報を全て取得する必要はなく、「新しい状態かどうか」だけ返してくれればbodyにリソースの情報を含めずに、手元にあるデータを再利用していいというお墨付きをサーバーから受け取ることが出来ます。

そのお墨付きを与えるパターンが以下の2つです。

  • 更新日時を利用する: Last-Modified/If-Modified-Since
  • Etagを利用する: If-None-Match

上記2つのパターンについてはCacheの有効性を確認するためにリクエストヘッダーに更新確認用の値を付与してリクエストをします。

更新日時を利用して最新状態を確認する

リソースの新旧のみを比較します。その比較のための目印として、更新日時 を利用します。
これは一番わかり易いと思いました。理由は、リソースが更新された場合は、リソース内部の UpdateAt が更新されているはずで、ここだけ見れば、そのリソースが新しい状態かどうかを確認できます。

この確認をするためには リクエストヘッダーにIf-Modified-Sinceをつけて、手元のリソースの最終更新時刻を値として付与してリクエス します。

If-Modifewd-Since: (手元のリソースのUpdateAt GMTのタイムゾーンを使用する)

変更がある(最終更新時刻が変更されている) 場合は、サーバーはそのまま通常通り200 OKを返してbodyにリソースの情報をいれてクライアントに返します。
変更がない(差一周更新時刻が変更されていない)場合は、サーバーは 304 Not Modified を返し、bodyはレスポンスに含まれません。この場合は、クライアントは手元にあるリソースを再利用可能なお墨付きを貰っている状態です。

Etagを利用して最新状態を確認する

更新日時を利用した最新状態の確認では、リソースのUpdateAtを見ることでリソースが最新状態にあるのかどうかを確認しましたが、Etagを使う方法もあります。 ※ Etag ... Entity Tag の略

この場合、リソースのデータ構造の設計時にEtagが入っていることが必要になります。

Etagをリクエストヘッダーに付与するときは If-None-Match ヘッダーを利用します。

If-None-Match: (Etag)

クライアントからリクエストする時にEtagを If-None-Match ヘッダーに付与することで、 - Etagが更新されてなければリソースに変化なしとしてリソースを取りに行かずに304 Not Modifiedを返し、bodyはレスポンスに含めません。 - Etagが更新されていれば、そのまま200OKを返して、bodyをレスポンスに含めます。

Expiresについて

文字通り 有効期限 です。
更新日時にしろ、Etagにしろ、かならずリソースの状態を確認するために、サーバーにアクセスしないといけません。リソースの状態によってレスポンスにbodyを含めるか、304のみを返すかの違いがあり、通信そのものを減らすことが出来る場合はありますが、結果として Cacheの有効性、リソースの状態を確認するための通信が発生する ことに変わりはありません。

この通信自体をなくしてしまうために、有効期限をもたせることが出来ます。
有効期限を持たせるには Expiresヘッダーを付与します。

ExpiresヘッダーにはCacheが有効な(期限切れになる) 日時 が格納されます。 ※ あとで記述するmax-ageは今からの相対秒数が格納されるのと混同しやすい

Expires: (期限切れになる日時 GMTのタイムゾーンを利用する)

ここで設定された日時はあくまでも、サーバーアクセスをするかどうかの判定にしか使われません。
(Real World HTTP)

このため、有効期限以前にアクセスした場合は、Cacheを自動的に使います。サーバーにはアクセスしません。X秒後の時間をしていても、X秒たった時点で自動的にサーバーに取り行くようなことはせず、古いコンテンツが使われ続けます。
X秒たったあとにリロードした場合にExpiresが切れているのでサーバーアクセスを行います。

動的に常に変更する要素には余り使うべきでなく、CSSやHTML内の滅多に更新されない要素に対して使うのが望ましいヘッダーです。

まとめ

とりあえずpart1としてざっとまとめてみました。Cacheについて実は挙動だけでなく、何がCacheなのか、リクエストヘッダーも含めたところから考える機会がなかったので一度おさらいしてみてよかったです。part2ではCacheControl周りを調べます。