性能優化-http緩存

以前對http的緩存知識只知其一;不知其二,只能說出個大概。和同事交流這塊內容時稍一深刻探討就捉襟見肘,自慚形穢。故痛定思痛,花了一兩天時間去研究了下這塊內容,寫下這篇筆記方便之後的查詢與修正。php

首先介紹下和緩存相關的http頭字段參數。

http的頭字段包括通用頭字段(general-header)、請求頭字段(request-header)、響應頭字段(response-header)以及實體頭字段(entity-header)。html

我我的對實體頭字段這個說法有點陌生,因此查閱了相關資料:根據RFC-2616(7.1 Entity Header Fields)中的解釋是:實體頭字段(entity header)定義了實體主體(entity-body)的元信息,若是實體主體(entity-body)不存在,則表示http請求所標識的資源的元信息。web

Entity-header fields define metainformation about the entity-body or,
if no body is present, about the resource identified by the request.算法

general header


Cache-Control: 整個請求響應鏈的緩存機制必須遵循的特別指令。這個指令是單向的,由於請求中該指令的效果和響應中的效果並不同。其值包括max-age,no-cache等。segmentfault

The Cache-Control general-header field is used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain. 瀏覽器

Cache directives are unidirectional in that the presence of a directive in a request does not imply that the same directive is to be given in the response.緩存

Date: 請求或響應的時間和日期。(注:這裏的日期格式使用的是GMT格式,即格林威治時間,要換算成北京時間的話要加上8小時)性能優化

Pragma:可被應用到整個請求響應鏈中的自定義設置,例如:Pragma: no-cache。服務器

The Pragma general-header field is used to include implementation-specific directives that might apply to any recipient along the request/response chain.網絡

request header


If-Modified-Since: 若是從該參數指定的時間開始,請求並無被修改的話,服務器會返回304(not modified)狀態碼,而不是一個實體。

if the requested variant has not been modified since the time specified in this field, an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message-body.

If-Unmodified-Since: 若是從該參數指定的時間開始,請求並無被修改的話,服務器應該執行請求的操做。若是被修改過的話,服務器不執行操做病返回412(Precondition Failed)狀態碼

If the requested resource has not been modified since the time specified in this field, the server SHOULD perform the requested operation as if the If-Unmodified-Since header were not present.

If the requested variant has been modified since the specified time, the server MUST NOT perform the requested operation, and MUST return a 412 (Precondition Failed).

If-None-Match: 容許在對應的內容未被修改的狀況下返回304未修改( 304 Not Modified ),判斷依據爲客戶端該參數存儲的Etag值與服務器上存儲的Etag是否相匹配。(原文太長,暫時拿wiki百科來填坑)

If-Match: 僅當客戶端提供的實體與服務器上對應的實體相匹配時,才進行對應的操做。主要用做像 PUT 這樣的方法中,僅當從用戶上次更新某個資源以來,該資源未被修改的狀況下,才更新該資源。若是不匹配,則返回412(Precondition Failed)狀態碼,該參數也會被忽略。(原文太長,暫時拿wiki百科來填坑)

response header


ETag: 指定請求變量的當前實體Tag。該字段用來比較來自同一資源的變化的實體。(只是直譯,後面會有詳細解釋和用法)

The ETag response-header field provides the current value of the entity tag for the requested variant.

Entity Header


Expires: 指定過時時間。

The Expires entity-header field gives the date/time after which the response is considered stale.

Last-Modified: 指定了服務器認爲資源最近一次修改的時間。

The Last-Modified entity-header field indicates the date and time at which the origin server believes the variant was last modified.

字段參數可能會有翻譯或者理解錯誤,請各位大大指出。

瞭解了參數的含義後,接下來就是看具體的緩存機制了。

HTTP 1.0時代 (基於Pragma&Expires的緩存實現)

Pragma在RFC1945文檔中只提供了「no-cache」值,當"no-cache"出如今請求消息中時,應用程序應當向原始服務器推送此請求,即便它已經在上次請求時已經緩存了一份拷貝。這樣將保證客戶端能接收到最權威的迴應。它也用來在客戶端發現其緩存中拷貝不可用或過時時,對拷貝進行強制刷新。
(HTTP1.1會把請求中的pragma="no-cache"視爲發送了Cache-Control: no-cache)

有Pragma用來禁用緩存的話,那就有Expires來設置緩存。
就如上文介紹所說,Expires是用來指定過時時間的,時間格式爲GMT,若是客戶端時間沒有超過該時間點的話,則不發送請求。可是值得注意的是,Expires所定義的緩存時間是相對服務器上的時間而言的,若是客戶端的時間和服務器的時間不一致的話(例如用戶修改了系統時間),那麼這個資源失效時間也沒有意義了。

HTTP 1.1時代

http1.1新增了 Cache-Control 來定義緩存過時時間,若報文中同時出現了Expires 和 Cache-Control,會以 Cache-Control 爲準。
如上文介紹所說,Cache-Control是通用頭部參數,格式爲

"Cache-Control" ":" cache-directive

做爲請求頭部時,cache-directive的可選值爲:(圖片來自網絡)

做爲響應頭部時,cache-directive的可選值爲:(圖片來自網絡)

cache-control設置緩存的使用方法爲:Cache-Control:max-age=31536000,緩存單位爲秒。

若是客戶端請求的資源未超過緩存時間,則直接取本地緩存,狀態碼爲200(from memory cache),
可是若是超過緩存時間或者直接不走緩存的話,那咱們就須要和其餘的字段配合來校驗緩存了。

1. Last-Modified

服務端傳遞資源給客戶端的時候會在響應報文中帶上這個信息,而客戶端接受到資源後會將該信息的值設置爲請求頭中If-Modified-Since參數的值(If-Modified-Since: Last-Modified value)。在下次發送請求的時候(max-age過時以後),會將請求頭中的If-Modified-Since參數的值與服務端Last-Modified的值去比較,若是還是一致的,那麼說明該資源未被修改,直接返回304狀態碼便可,若是不一致,則返回狀態碼200,以及新的Last-Modified。

可是Last-Modified會有不許確的時候,好比服務端的資源作了無實際變化的修改(加一個空格再刪掉),這樣也會使服務端Last-Modified的時間更新,致使客戶端請求中If-Modified-Since的值與其沒法匹配,就會返回整個實體。(即便返回的實體內容與客戶端的緩存內容沒有區別)

2. Etag

Etag是一種比Last-Modified更爲精確的校驗方式。服務器會經過某種算法,給資源計算得出一個惟一標誌符。其判斷依據和Last-Modified相似,只是服務端在傳遞資源給客戶端的相應報文中帶上Etag後,客戶端會將其設置爲請求頭中If-None-Match參數的值(If-None-Match: Etag-value),而後在傳遞資源時進行比較。

若是同時使用Last-Modified和Etag進行驗證的話,那麼二者中有一個經過校驗(即傳遞的值能夠匹配),則能夠認爲資源並無被修改。

基本的緩存機制就是由這些參數造成的。

最後咱們來看下,不一樣的頁面打開方式對緩存機制的影響

主要有如下兩點要注意:

1.手動刷新頁面(F5刷新),瀏覽器會直接認爲緩存已通過期,即便緩存並無過時,在請求中加上字段:Cache-Control:max-age=0,發包向服務器查詢是否有文件是否有更新。

2.強制刷新頁面(ctrl+F5刷新),瀏覽器會直接忽略本地緩存內容,即便本地有緩存可用,在請求中加上字段:Cache-Control:no-cache(或 Pragma:no-cache),發包向服務器從新請求文件。
其餘的直接上圖(圖片來自網絡):

此篇文章的不少想法是借鑑網上高手的文章,我會在底部標明參考出處。若是此文章中有什麼問題的話,煩請必定要指出,謝謝!

參考資料
RFC2616
H5 緩存機制淺析 移動端 Web 加載性能優化
淺談瀏覽器http的緩存機制

相關文章
相關標籤/搜索