2009年10月6日 星期二

HTTP Caching 優化網站

HTTP Caching 用好了,可以極大的減小服務器負載和減少網絡帶寬。十分有必要深入瞭解下 http 的 caching 協議。

先來看下請求/響應過程:

http 请求/响应

http 請求/響應

1、用 Last-Modified 頭

在第一次請求的響應頭返回 Last-Modified 內容,時間格式如:Wed, 22 Jul 2009 07:08:07 GMT。是零時區的 GMT 時間,servlet 中可以用 response.addDateHeader("Last-Modified", date.getTime()); 加入響應頭。如圖:

last-modified 和 If-Modified-Since

last-modified 和 If-Modified-Since

Last-Modified 與 If-Modified-Since 對應的,前者是響應頭,後者是請求頭。服務器要處理 If-Modified-Since 請求頭與Last-Modified 對比看是否有更新,如果沒有更新就返回 304 響應,否則按正常請求處理。如果要在動態內容中使用它們,那就要程序來處理了。

ps:servlet 取 If-Modified-Since 可以用 long last = requst.getDateHeader("If-Modified-Since");

2、用 Etag 頭

很多時間可能不能用時間來確定內容是否有更新。那可以用 Etag 頭,etag 是以內容計算一個標識。計算的方式可以自己決定,比如可以用 crc32、md5等。

Etag 和 If-None-Match

Etag 和 If-None-Match

Etag 與 If-None-Match 是對應的,前者是響應頭,後者是請求頭。服務器要判斷請求內容計算得到的 etag 是否與請求頭 If-None-Match 是否一致,如果一致就表示沒有更新,返回304就可,否則按正常請求處理。可以參考:用 HttpServletResponseWrapper 實現 Etag 過濾器

3、用 Expires 頭,過期時間

當請求的內容有 Expires 頭的時候,瀏覽器會在這個時間內不去下載這個請求的內容(這個行為對 F5 或 Ctrl+F2 無效,用 IE7,Firefox 3.5 試了,有效的比如:在地址輸入後回車)。

expires 过期时间

expires 過期時間

在 servlet 中可以用 response.addDateHeader("Expires", date.getTime()); 添加過期內容。

ps:在 httpwatch 中可以看到 Result 為 (Cached) 狀態的。

4、用 max-age 的 Cache-Control 頭

max-age 的值表示,多少秒後失效,在失效之前,瀏覽器不會去下載請求的內容(當然,這個行為對 F5 或 Ctrl+F2 無效)。比如:服務器寫 max-age 響應:response.addHeader("Cache-Control", "max-age=10");

ps:如果你還要加一些 Cache-Control 的內容,比如:private,最好不要寫兩個 addHeader,而是一個 response.addHeader("Cache-Control", "private, max-age=10"); 否則 ie 可能對 max-age 無效,原因它只讀第一個 Cache-Control 頭。

小結:

Last-Modified 與 Etag 頭(即是方式1和2)還是要請求服務器的,只是僅返回 304 頭,不返回內容。所以瀏覽怎麼 F5 ,304 都是有效的。但用 Ctrl+F5 是全新請求的(這是瀏覽器行為,不發送緩存相關的頭)。

Expires 頭與 max-age 緩存是不需要請求服務器的,直接從本地緩存中取。但 F5 會忽視緩存(所以使用 httpwatch 之類的 http 協議監察工具時,不要 F5 誤認為 Expires 和 max-age 是無效的)。

http 協議監察工具:

Firebox:httpfox、live http header

IE:httpwatch、iehttpheader

網頁的緩存是由HTTP消息頭中的「Cache-control」來控制的,常見的取值有private、no-cache、max-age、must-revalidate等,默認為private。其作用根據不同的重新瀏覽方式分為以下幾種情況:

(1) 打開新窗口
值為private、no-cache、must-revalidate,那麼打開新窗口訪問時都會重新訪問服務器。
而如果指定了max-age值,那麼在此值內的時間裡就不會重新訪問服務器,例如:
Cache-control: max-age=5(表示當訪問此網頁後的5秒內再次訪問不會去服務器)

(2) 在地址欄回車
值為private或must-revalidate則只有第一次訪問時會訪問服務器,以後就不再訪問。
值為no-cache,那麼每次都會訪問。
值為max-age,則在過期之前不會重複訪問。

(3) 按後退按扭
值為private、must-revalidate、max-age,則不會重訪問,
值為no-cache,則每次都重複訪問

(4) 按刷新按扭
  無論為何值,都會重複訪問

這邊也有解說 :http://webamp.giga.net.tw/blog.php?p=20

====================================================

主要重點在於我們要明白一個相對(Expires)一個絕對(max-age).

分別

max-age
max-age是HTTP/1.1中,他是指我們的web中的文件被用戶訪問(請求)後的存活時間,是個相對的值,相對Request_time(請求時間).
例如:A.html 用戶請求時間是18:00,max-age設置的是600的話,相當18:00+600秒過期,也就是相對18:00的時間後面600秒後過期.默認的max-age是由Expires算出來的.

Expires
Expires是HTTP/1.0中的,它比max-age要麻煩點.Expires指定的時間分下面二種,這個主要考慮到apache中設置是A還是M.

1.相對文件的最後訪問時間(Atime)
當Apache使用A時間來做Expires時.這樣設置時.他就和max-age的值相等,因為max-age是相對文件的請求時間(Atime).

例如:ExpiresByType text/html A600

由上面我們得知,Apache設置Atime時,過期為600秒時.
Expires=18:00+600=18:10
max-age=18:00+600=18:10
得出:Expires=max-age

2.絕對修改時間(MTime)
這又分二種情況,我們來拿A.htm來講
假設文件的建立時間為18:00.

當用戶Request請求為18:00時,過期為600秒
Expires=18:00+600=18:10
max-age=18:00+600=18:10
得出:Expires等於max-age

當用戶Request請求為18:20時,過期為600秒

Expires=18:00+600=18:10(因為設置成Mtime時,時間由文件建立時間來決定)
max-age=18:20+600=18:30
得出:Expires不等於max-age

另外要注意,象上面這種清況時,max-age優化,所以過期時間為18:30.

在squid,如果沒有指明expires和max-age這二個的截止時間,那它就會使用髮式截止時間,如參考 Last-Modified.
其實上面的max-age=18:20+600=18:30,這樣算max-age不對,真實環境要這樣算,max-age過期為http頭中的Age=600過期.
注:Age域值是緩存服務器估計從響應產生或被原始服務器重新證實以來的總時間.age的值是緩存服務器算出來的,原始服務器是沒有的.

【下列文章您可能也有興趣】

沒有留言: