実例から入ります。
http://xxxxxxxx/をリクエストします。
このURLへのアクセスは始めてです。

以前、書いた Firefoxのプラグイン Live HTTP Headers でHTTP通信のみをトレースしました。

———————————————————-
以下がリクエスト内容(初回アクセス)
———————————————————-

CODE:

  1. GET / HTTP/1.1
  2. Host: xxxxxxxx
  3. User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; ja; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
  4. Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
  5. Accept-Language: ja,en-us;q=0.7,en;q=0.3
  6. Accept-Encoding: gzip,deflate
  7. Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
  8. Keep-Alive: 300
  9. Connection: keep-alive

———————————————————-
以下がレスポンス内容(初回アクセス)
———————————————————-

CODE:

  1. HTTP/1.x 200 OK
  2. Date: Mon, 07 May 2007 11:49:33 GMT
  3. Server: Apache/2.2.4 (Unix)
  4. Last-Modified: Sat, 20 Nov 2004 20:16:24 GMT
  5. Etag: “4c602bd-2c-4c23b600″
  6. Accept-Ranges: bytes
  7. Content-Length: 44
  8. Connection: close
  9. Content-Type: text/html

そこでリロードして、再度リクエストを出しました。

———————————————————-
以下がリクエスト内容(2回目のアクセス)
———————————————————-

CODE:

  1. GET / HTTP/1.1
  2. Host: xxxxxxxx
  3. User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; ja; rv:1.8.1.3) Gecko/20070309 Firefox/2.0.0.3
  4. Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
  5. Accept-Language: ja,en-us;q=0.7,en;q=0.3
  6. Accept-Encoding: gzip,deflate
  7. Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
  8. Keep-Alive: 300
  9. Connection: keep-alive
  10. If-Modified-Since: Sat, 20 Nov 2004 20:16:24 GMT
  11. If-None-Match: "4c602bd-2c-4c23b600"
  12. Cache-Control: max-age=0

———————————————————-
以下がレスポンス内容(2回目のアクセス)
———————————————————-

CODE:

  1. HTTP/1.x 304 Not Modified
  2. Date: Mon, 07 May 2007 11:51:27 GMT
  3. Server: Apache/2.2.4 (Unix)
  4. Connection: close
  5. Etag: “4c602bd-2c-4c23b600″

ここで注目は
1)初回アクセスのレスポンス中の

CODE:

  1. Etag: “4c602bd-2c-4c23b600″

2)2回目アクセスのリクエスト中の

CODE:

  1. If-None-Match: “4c602bd-2c-4c23b600″

3)2回目アクセスのレスポンス中の

CODE:

  1. HTTP/1.x 304 Not Modified
  2. Etag: “4c602bd-2c-4c23b600″

です。

このEtagとはエンティティTAGのことで、apacheのマニュアルからから引用すると、

FileETag ディレクティブは ドキュメントがファイルに基づいたものであるときに、 ETag (エンティティタグ) 応答ヘッダフィールドを作成するときに使用する ファイルの属性を設定します。 (ETag の値はネットワークの帯域を節約するための キャッシュの管理で使われます。) Apache 1.3.22 以前では、ETag の値は 常にファイルの inode, サイズ、最終修正時刻 (mtime) から作成 されていました。FileETag ディレクティブにより、これらのどれを使うかを 選ぶことができます。

となります。

つまり、

  1. 静的コンテンツ(html、画像、css、javascript等)をapacheに要求するとapacheはinode, サイズ、最終修正時刻 (mtime) からEtagの値を算出してレスポンスに含めます。
  2. ブラウザはあるページにアクセスしようとした時に、それが自分のキャッシュにあると、If-None-Matchヘッダーに以前のEtagの値を付与してサーバーに送信します。
  3. apacheはリクエストを受け取って、そのEtagの値が改めて算出し直したEtagの値と等しければ「304 Not Modified」を返し、異なっていれば、レスポンスコード200と実際のコンテンツを返却します。

※ここではIf-Modified-Sinceのことは省略しています。

WWWサーバーが一台であれば、コンテンツファイルは1個しかないので、「ファイルの実際の更新」=「Etagの変更というか変化」になるので問題ないのですが、負荷分散ルーター配下にWWWサーバーが複数台あって個別にファイルを配置している場合、まったく同じ内容のファイルを置いても、どうしてもinodeが異なるゆえに異なるEtagになってしまいます。

ファイルが変更されたと認識されて、 「304 Not Modified」ではなく、レスポンスコード200と実際のコンテンツが返却されてしまいます。

例えば、負荷分散ルーターの振り分け論理がラウンドロビンだったりすると、アクセスするたびに、別のEtagになってしまい、必要以上の負荷をネットワークに与えることになります。

これを解決するために

Etagの生成をサイズ、最終修正時刻 (mtime) からのみに指定

CODE:

  1. FileETag MTime Size

とか

Etag自体を返却しない(この場合If-None-Matchヘッダーも送信されなくなる)

CODE:

  1. FileETag None

等の対応策をとります。

さらに詳しい話はまた次回に。

 

関連会社インターオフィスのブログ記事を転載しています。