HTTP/1.1 の「条件付きGET」を利用して PHP ファイルアクセスによるサーバ負荷を削減する

HTTP/1.1 の「条件付きGET」を利用して PHP ファイルアクセスによるサーバ負荷を削減する

Posted at January 15,2007 12:50 AM
Tag:[Performance, PHP]

Movable Type 等で PHP 化PHP モジュール化によりファイルの拡張子を .php で運用している場合の、サーバ負荷・ネットワークトラフィックを削減する方法をご紹介します。

1.問題点

HTML ファイルの拡張子を .php にしている場合、HTTP/1.1 で規定されている「条件付き GET」が行われません。ブラウザはこのようなサイトに対し無条件に GET を行ってしまうため、サーバ負荷やネットワークトラフィック増加の要因の一つになっています。

2.条件付き GET とは

「条件つき GET」は RFC2616(HTTP/1.1) 9.3 で定義されています。以下和訳を引用します。

RFC2068 9.3 GET(RFC2616は更新版)

GET メソッドは Request-URI で識別される (エンティティの形式においての)情報ならなんでも回収する事を意味する。もし Request-URI が data-producing プロセスを参照しているなら、それはリソースのエンティティとして返されるであろう作られたデータである。これはもしそのテキストがプロセスの出力で生じるのでなければ、プロセスのソーステキストではない。

もし If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Matchや If-Range ヘッダフィールドをリクエストメッセージが含んでいるなら、GET メソッドのセマンティクスは "条件付き GET" に変わる。条件付き GETメソッドはエンティティがその条件付きヘッダフィールドによって表される状況の元でのみ転送される事をリクエストする。条件付き GET メソッドはキャッシュされるエンティティに複数のリクエストを必要としたりクライアントによってすでに保持されたデータを転送する事なしに再び新しくされる事を可能にする事により、ネットワークの不必要な使用を減少する目的を持つ。

要するに、「前回アクセス以降からページの更新が行われていない場合はサーバから改めてデータを送信しない仕組みがある」というようなことが書かれています。

3.条件付き GET の振る舞い

2項の説明だけでは良く分からないので、Firefox 2.0 の Live HTTP Headers で、当サイトのトップページにアクセスした時の HTTP ヘッダを例に、具体的な動作を説明します。

3.1 条件付きGETなしの場合

リスト3.1.1 リクエストヘッダ(条件付きGETなし)

GET / HTTP/1.1
Host: www.koikikukan.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: 略
Cache-Control: max-age=0

リスト3.1.2 レスポンスヘッダ(条件付きGETなし)

HTTP/1.x 200 OK
Date: Sun, 14 Jan 2007 14:21:19 GMT
Server: Apache/1.3.37 (Unix)
X-Powered-By: PHP/4.4.4
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8

条件付き GET が行われていない状態では、リクエスト・レスポンスは上記のようなヘッダになります。この状態ではアクセスする度に 200 OK を返却、つまりページが更新・未更新に関わらず、サーバは常にデータ(リクエストボディ)を返却します。

3.2 条件付きGETあり(ブラウザで初回アクセス時)

リスト3.2.1 リクエストヘッダ(条件付きGETあり:初回アクセス)

GET / HTTP/1.1
Host: www.koikikukan.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: 略
Cache-Control: max-age=0

リスト3.2.2 レスポンスヘッダ(条件付きGETあり:初回アクセス)

HTTP/1.x 200 OK
Date: Sun, 14 Jan 2007 14:22:45 GMT
Server: Apache/1.3.37 (Unix)
Etag: "cc8929434ffafd940e5956b71a0be1f5"
X-Powered-By: PHP/4.4.4
Last-Modified: Sun, 14 Jan 2007 14:22:32 GMT
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8

条件付き GET(というか今回のカスタマイズ)が行われている場合、HTTP レスポンスに Etag / Last-Modified フィールドを付与して返却します。Etag は更新時刻などから生成されたハッシュ値、Last-Modified は更新時刻です。
リクエストを送信したブラウザはこの情報をページ単位に保持します(200 OK なのでこの時はリクエストボディも同時に返却します)。

3.3 条件付き GET あり(2回目以降のアクセス)

リスト3.3.1 リクエストヘッダ(条件付きGETあり:2回目以降)

GET / HTTP/1.1
Host: www.koikikukan.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: 略
If-Modified-Since: Sun, 14 Jan 2007 14:22:32 GMT
If-None-Match: "cc8929434ffafd940e5956b71a0be1f5"
Cache-Control: max-age=0

リスト3.3.2 レスポンスヘッダ(条件付きGETあり:2回目以降)

HTTP/1.x 304 OK
Date: Sun, 14 Jan 2007 14:23:07 GMT
Server: Apache/1.3.37 (Unix)
Etag: "cc8929434ffafd940e5956b71a0be1f5"
X-Powered-By: PHP/4.4.4
Last-Modified: Sun, 14 Jan 2007 14:22:32 GMT
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8

2回目以降のアクセスでは、前回アクセスしたレスポンスヘッダに含まれる Etag / Last-Modified フィールドの値を、今回のリクエストヘッダの If-Modified-Since / If-None-Match フィールドに設定して送信します。

リクエストを受信した HTTP/1.1 サーバでは、サーバ自身が保持するページ情報と比較して、ページの更新・未更新を判断します(この辺りは推測で書いてます。間違っていたらすいません)。

「304 Not Modified」はページが更新されていない時に返却するレスポンスコードです(304 にレスポンスボディは含まない仕様です)。304 レスポンスを受信したブラウザはサーバからデータをダウンロードせず、ローカルキャッシュを表示します。

つまり、PHP ファイルで「条件付き GET」を有効にし、304 Not Modified を返却できるようにすれば、結果的にサーバ・ネットワークの負荷を削減することができる、という訳です。

4.対処方法

対処方法は下記のサイトで紹介されています。当サイトでも利用させて頂きました。ありがとうございました。

Ogawa::Memoranda「条件付きGET」のススメ

上記は Movable Type 利用で

  • スタティックページ生成
  • ページを PHP モジュール化
  • サイドバーの部品を PHP を利用したインクルード

を前提にされています。

ダイナミックパブリッシングを利用している場合はデフォルト機能として、条件付き GET が利用できます(関連記事「Movable Type の再構築を不要にする「ダイナミック・パブリッシング」(その2:設定方法)」)。

5.その他のページについて

該当ページに含まれる外部ファイルは全て HTTP によって取得されますが、

  • CSS(.css)
  • JavaScript(.js)
  • 画像(.jpg / .gif / .png 等)

は HTTP/1.1 サーバで 304 を返却しています。例えば CSS を修正すると、スタイルシートファイルだけが新たに読み込まれます。この仕組みのお陰で、当サイトのように外部ファイルを大量に読み込んでいるページでも、タイムラグの少ない表示ができるという訳です。

6.参考

関連記事
zenback
人気エントリー
トラックバックURL


トラックバック

今週の話題 : SVD によるリコメンデーションシステムなど from WebOS Goodies
先ほど書店に立ち寄ったところ、 Software Design の 2 月号が発売されていて、 BIND の特集が組まれていたので買って... [続きを読む]

Tracked on January 20, 2007 9:02 PM

HTTP/1.1 の「条件付きGET」を導入 from Ricordo
.php化、php化モジュールをしているとサーバにも負荷がかかりBlog表示速度も遅くなる [続きを読む]

Tracked on January 31, 2007 2:32 AM

Movable Type PHP化による負荷を軽減(HTTP 1.1 条件付きGET) from Jay's Room
MovableTypeをPHP化している場合には、 HTTP1.1で規定されてい... [続きを読む]

Tracked on January 31, 2007 10:43 AM

HTTP/1.1 の「条件付きGET」を利用して PHP ファイルアクセスによるサーバ負荷を削減 from KINGO WEBlog
「Movable TypeのPHP化」を実施したわけだけれども、HTML ファイ... [続きを読む]

Tracked on February 5, 2007 12:50 PM

Movable Type: PHP 化した from Bowz::Notebook
サイドバーに「最近のコメント」とかを追加すべく、Movable Type で管理しているブログを PHP 化した。 PHP 化の方法 メインページ・アー... [続きを読む]

Tracked on April 12, 2007 11:23 AM

HTTP/1.1の「条件付きGET」を利用してPHPファイルアクセスによるサーバ負荷を削減 from ネコの為に鈴は鳴る。 - After☆Taste Blog
重たいのを少しでも軽減出来ないかとアレコレ検索して辿り着きました。 HTTP/1.1の「条件付きGET」を利用してPHPファイルアクセスによるサーバ負荷を... [続きを読む]

Tracked on May 1, 2007 1:15 AM
コメント

こんにちわ^^

条件付きGETなんてことができるんですねっ!!
いろいろ考えられてるんだなぁ・・・と感心するばかりです(; ̄∇ ̄A

[1] Posted by bzbell : January 15, 2007 11:31 PM

>bzbellさん
こんばんは。
コメントありがとうございます。
この技、かなり良好です。

[2] Posted by yujiro : January 17, 2007 1:31 AM

こんちわ^^

この記事参考にわたしもやってみました( ̄∇ ̄)/
かなり有効ですねっ!!

これでまた思う存分カスタマイズして楽しめそうです♪

これからも参考にさせていただきます (m;_ _)mペコ

[3] Posted by bzbell : January 17, 2007 11:31 PM

>bzbellさん
こんにちは。
記事参照&ご連絡ありがとうございました。
表示がかなり早くなったみたいですね!

私の方も参考にさせて頂いてます。
これからもよろしくお願い致します。

[4] Posted by yujiro : January 18, 2007 1:29 PM

大変参考になります。私もPHPモジュール化しております。
ただ、「」などしてインクルードしておりますので
単純ケースでは不十分のようですね。
よって、少し複雑ケースにしなくてはいけないようなのですが(理解はこれで良いのでしょうか?また、他に簡単な方法があればそちらを参考にしたいのですが・・・・)
そこで、この修正をする為に数箇所意味がわかりにくい点があり確認とアドバイス
頂きたいところがあります。
まず、「以下のコードをファイルの先頭に記述する」とありますがこの意味は
モジュール化したファイルのタグ先頭に追記する!とのとらえかたで良いのでしょうか
また、複雑ケースでの説明で「「left-column.php」、「right-column.php」は、インクルードしているファイル名に適宜読み替えてください。」とありますがこの箇所で「ファイル名を読み替える」部分が充分理解できません。
具体的にどのように修正すればよいのかアドバイス頂ければ幸いだと思います。
宜しくお願いします。

[5] Posted by panser : January 19, 2007 1:48 PM

追記

前文コメントで一部表示されていない部分があった為再送させて頂きました。下記部分です。宜しくお願いします。

ただ、「<?php readfile("..."); ?>」などしてインクルードしておりますので単純ケースでは不十分のようですね。
[6] Posted by panser : January 19, 2007 1:56 PM

>panserさん
こんばんは。
ご質問の件ですが、「以下のコードをファイルの先頭に記述する」の「ファイル」は HTML ファイルです。つまりテンプレートの1行目にコードを挿入します。

「ファイル名を読み替える」の「ファイル」は、panser さんが readfile を利用してインクルードしている行(下記)の赤色部分を指してます。

readfile("[ファイル名]");

いかがでしょうか?

[7] Posted by yujiro : January 20, 2007 3:33 AM

時間をみてまた、チャレンジしてみます。丁寧なアドバイスありがとうございました。

[8] Posted by panser : January 20, 2007 3:46 AM

こんにちわ。

いつも大変参考にさせていただいています。本も購入させていただきました。

おかげさまでMTの導入がスムーズに出来て大変感謝しています。

php化は成功しました。しかし、最近、500インターネットサーバーエラーになり、条件付きGETを導入したいのですが、いまいちよくわかりません。

Ogawa::Memoranda:「条件付きGET」のススメの方にも行きましたが、どこをどうすればいいか・・・

初心者ですみません。。

MT4.1、でディフォルトのテンプレートを使用しています。

ディフォルトテンプレートが既にモジュール化?されており、複雑で・・・

Ogawa::Memoranda:「条件付きGET」の、複雑なケースの方を、全てのファイルの上部に追加すればよいのでしょうか?

もし、よろしければ、ご教授くださいませ。

[9] Posted by maki : March 3, 2008 2:30 PM

>makiさん
こんばんは。
ご質問の件ですが、その通りです。
例えば、当サイトのメインページテンプレートは下のようになっています(条件付きGET用のPHPのモジュール化はしていません)。

<?php
$ts = getlastmod();
doConditionalGet($ts);
  :
 (略)
  :
  exit;
}
?>
<? echo('<?xml version="1.0" encoding="utf-8"?>') ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="ja" xml:lang="ja" xmlns="http://www.w3.org/1999/xhtml">
<head>
  :
 (略)
  :

デフォルトテンプレートであれば、メインページやブログ記事リスト、ブログ記事、ウェブページの各テンプレートにある

<$MTInclude module="ヘッダー"$>

の直前に埋め込めば大丈夫と思います。
それではよろしくお願い致します。

[10] Posted by yujiro logo : March 4, 2008 1:39 AM

こんにちわ、ご丁寧にありがとうございます。

Ogawa::Memoranda:「条件付きGET」のススメのの、少し複雑なケースの、サイトパスを指定したもの(このサイトパスがよくわかりません)を、全てのページに埋め込めばいいのでしょうか?

ここが微妙にわからなくて、

Ogawa::Memoranda:「条件付きGET」のススメの、単純なケースを埋め込んだり、いろいろやってみましたが、成功しません。。

とりあえず、埋め込んで、再構築して、ブログを開いて確認すると、真っ白な状態で、ブログが表示されなくなってしまいます。

現在、記事を書く度に、500エラーになってしまう(エラーになるが、記事は投稿できます)ので、このエラーをどうにか修正したいところですが・・・

素人がphp化などしない方がよかったのかな・・・と少し後悔してしまったり…

お時間を取らせてしまってすみません。。

[11] Posted by maki : March 4, 2008 12:05 PM

上記コメントの追記です。

Ogawa::Memoranda:「条件付きGET」のススメでの単純なケースのソースをメインページやブログ記事リスト、ブログ記事、ウェブページに埋め込めました。

1度メモ帳に貼ってから、貼り付けたら真っ白くならずにブログが表示されました。

ただ、単純なケースのソースではよくないですよね・・・???

まだ、500エラーは直りません。。

それにしても、MTの500エラーは多い問題のようですね。

利用している、エックスサーバーにも問い合わせましたが、解決には至りませんでした。。

[12] Posted by maki : March 4, 2008 4:25 PM

>makiさん
こんばんは。
どのようなテンプレート構造になっているか不明ですが、PHPモジュール化していなければ「単純なケース」で大丈夫です。
再構築の500エラーはサーバのパフォーマンスが大きく影響するようです。エックスサーバーは使ったことはないのですが、XREAやさくらインターネットは結構大丈夫です。
それではよろしくお願い致します。

[13] Posted by yujiro logo : March 7, 2008 2:13 AM
コメントする
greeting

*必須

*必須(非表示)


ご質問のコメントの回答については、内容あるいは多忙の場合、1週間以上かかる場合があります。また、すべてのご質問にはお答えできない可能性があります。予めご了承ください。

太字イタリックアンダーラインハイパーリンク引用
[サインインしない場合はここにCAPTCHAを表示します]

コメント投稿後にScript Errorや500エラーが表示された場合は、すぐに再送信せず、ブラウザの「戻る」ボタンで一旦エントリーのページに戻り(プレビュー画面で投稿した場合は、投稿内容をマウスコピーしてからエントリーのページに戻り)、ブラウザをリロードして投稿コメントが反映されていることを確認してください。

コメント欄に(X)HTMLタグやMTタグを記述される場合、「<」は「&lt;」、「>」は「&gt;」と入力してください。例えば「<$MTBlogURL$>」は「&lt;$MTBlogURL$&gt;」となります(全て半角文字)