53.HTTP緩存詳解

轉載請註明原始鏈接:http://blog.csdn.net/a464057216/article/details/52780055。本篇博客用Mac OS X Sierra上的Firefox 47.0.1版本做的實驗,對於其他瀏覽器或版本可能會有不同。

簡介

緩存主要分爲兩類:服務端緩存、客戶端緩存。
服務端緩存包括轉發代理服務器緩存、反向代理服務器緩存、CDN緩存等。
客戶端緩存一般指瀏覽器緩存,比如Chrome瀏覽器查看緩存可以訪問chrome://cache

瀏覽器緩存機制

主要分爲兩種:通過HTML <meta>標籤和通過HTTP報文頭。

HTML meta標籤

<meta http-equiv="pragma" content="no-cache" />用來告訴瀏覽器每次想要使用緩存前都需要到服務器驗證緩存是否有更新。並不是所有瀏覽器都支持這個功能,緩存服務器也不會解析HTML文件的內容,所以一般不要使用這種方式。

HTTP 頭部信息

expires字段

HTTP 1.0中,服務器通過迴應給客戶端的HTTP Response的頭部的expires字段說明資源的過期時間,使用該字段容易受到服務器與客戶端時區及時間差異的影響。比如在服務端使用Flask將響應的緩存有效期設置爲當前時間的18小時以後:

# Written by - CSDN: Mars Loo的博客

GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'

@app.route('/', methods=["GET",])
def index():
    r = make_response(render_template('index.html'))
    exp = datetime.datetime.utcnow() + datetime.timedelta(hours=18)
    r.headers['Expires'] = exp.strftime(GMT_FORMAT)

    return r.make_conditional(request)

在Mac OS X Sierra上使用Firefox 47.0.1訪問該地址:
這裏寫圖片描述
在服務端修改index.html文件的內容,然後在瀏覽器地址欄重新輸入地址後訪問,發現並沒有加載最新的服務器端內容(狀態碼200前面灰色的圓圈表示加載的是瀏覽器緩存):
這裏寫圖片描述

通過cache-control字段控制緩存策略

HTTP 1.1標準中設計了cache-control字段用於替代expires字段(如果兩者同時存在,以cache-control的設置爲準),cache-control可以實現比expires更精細的緩存控制比如:

  • max-age:緩存的過期時間(以秒爲單位)。
  • public:響應可以被任何緩存區域緩存,比如客戶端瀏覽器、代理服務器等。
  • private:響應的部分或全部內容屬於一個單獨的用戶,只允許客戶端瀏覽器緩存,不允許代理服務器緩存。
  • no-cache:無論如何,客戶端瀏覽器必須先與服務器重驗證緩存中的響應是否被更改,然後才能使用該響應來滿足後續對同一個網址的請求。

如果之前的響應包含ETag字段,可能會使用If-Match字段或If-None-Match字段驗證;如果之前的響應包含Last-Modified字段,可能會使用If-Modified-Since字段或If-Unmodified-Since字段驗證。比如服務端設置了no-cache、30秒的max-ageETag字段:

# Written by - CSDN: Mars Loo的博客

@app.route('/', methods=["GET",])
def index():
    r = make_response(render_template('index.html'))
    r.add_etag()
    r.cache_control.max_age = 30
    r.cache_control.no_cache = True

    return r.make_conditional(request)

使用FF訪問該頁面:
這裏寫圖片描述
30秒之內在地址欄中輸入地址敲回車後,瀏覽器向服務器發送請求重驗證,如果資源沒有發生過修改,返回304 Not Modified,瀏覽器從緩存加載頁面:
這裏寫圖片描述

  • must-revalidate:如果資源過期,客戶端瀏覽器必須先與服務器確認緩存中的響應是否被更改,然後才能使用該響應來滿足後續對同一個網址的請求(各個瀏覽器本身的實現也應該如此,只是must-revalidate將這一過程具體化了)。驗證方法同上。比如服務端設置了Last-Modifiedmax-agemust-revalidate屬性:
# Written by - CSDN: Mars Loo的博客

GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'

@app.route('/', methods=["GET",])
def index():
    r = make_response(render_template('index.html'))
    r.last_modified = datetime.datetime.utcnow()
    r.cache_control.max_age = 30
    r.cache_control.must_revalidate = True

    return r.make_conditional(request)

FF瀏覽器第一次訪問:
這裏寫圖片描述
30秒之內在地址欄中輸入地址敲回車後,瀏覽器會讀取緩存:
這裏寫圖片描述
30秒之後在地址欄中輸入地址敲回車後,瀏覽器會去服務端驗證緩存是否過期。從上面服務端的代碼看,意味着下次認證會返回資源已經發生了修改,響應如下:
這裏寫圖片描述

將服務端資源改成Last-Modified沒有發生更改的情形:

# Written by - CSDN: Mars Loo的博客

GMT_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'

lm = datetime.datetime.utcnow()
@app.route('/', methods=["GET",])
def index():
    r = make_response(render_template('index.html'))
    r.last_modified = lm
    r.cache_control.max_age = 30
    r.cache_control.must_revalidate = True

    return r.make_conditional(request)

在30秒緩存過期後請求同樣的地址,響應爲304 Not Modified,客戶端瀏覽器加載緩存:
這裏寫圖片描述

  • no-store:禁止任何緩存區域緩存響應。

比如服務端設置了no-store

# Written by - CSDN: Mars Loo的博客

@app.route('/', methods=["GET",])
def index():
    r = make_response(render_template('index.html'))
    r.add_etag()
    r.cache_control.no_store = True
    return r.make_conditional(request)

使用FF瀏覽器訪問該頁面:
這裏寫圖片描述
第二次在地址欄中輸入地址敲回車後,瀏覽器還是會向服務器發送請求:
這裏寫圖片描述

Etag/If-None-Match

ETag可以理解爲資源的摘要簽名信息,如果資源未發生改變,該簽名信息是不會改變的。客戶端第一次請求服務端的某靜態文件時,服務端可以在ETag字段迴應資源的簽名信息。接下來的請求中,如果客戶端判斷緩存已經過期(超過了max-age),會在If-None-Match字段填充緩存的ETag信息詢問服務端資源是否發生過變動,如果發生過變化,服務端迴應200的正常響應,如果未發生過變化,返回304 Not Modified響應,瀏覽器加載緩存,304響應不包含Response的body部分,提高了傳輸效率。

Last-Modified/If-Modified-Since

Last-Modified是客戶端第一次請求服務端的某靜態文件時,服務端填充的資源最後修改時間。接下來的請求中,如果客戶端判斷緩存已經過期(超過了max-age),會在If-Modified-Since字段填充緩存的Last-Modified信息詢問服務端資源是否發生過變動,如果發生過變化,服務端迴應200的正常響應,如果未發生過變化,返回304 Not Modified響應。

ETag與Last-Modified

使用Last-Modified字段可能有如下缺點:資源內容未發生變化,但是最後修改時間變了,造成帶寬浪費;只能精確到秒級,如果資源在1秒內被多次修改,不能準確體現資源變化,造成客戶端獲取的數據不準確。ETag可以精確表示資源是否發生變化,當兩個字段同時存在時,以ETag爲準。

緩存與瀏覽器刷新的關係

比如Mac上FF瀏覽器有Command+r普通刷新和Shift+Command+r強制刷新。普通刷新相當於瀏覽器在發往Server端的請求頭中添加字段Cache-Control:max-age=0,表示從客戶端到服務端的沿途服務器都需要重新校驗緩存:
這裏寫圖片描述
強制刷新相當於瀏覽器在發往服務器的請求頭中添加Pragama: no-cacheCache-Control: no-cache頭,表示客戶端到服務端的節點無需重新驗證緩存是否過期而是強制要求服務端返回一個非緩存的版本(即使資源未發生變化,也不返回304 Not Modified,而是返回200的正常響應):
這裏寫圖片描述

如果覺得我的文章對您有幫助,歡迎關注我(CSDN:Mars Loo的博客)或者爲這篇文章點贊,謝謝!

如下幾篇文章是我在學習HTTP協議時總結的博文,歡迎參考:
1. HTTP代理及重定向
2. HTTP認證(基本認證與摘要認證)
3. HTTP、HTTPS基本原理

發佈了83 篇原創文章 · 獲贊 185 · 訪問量 50萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章