轉載請註明原始鏈接: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-age
和ETag
字段:
# 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-Modified
、max-age
和must-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-cache
和Cache-Control: no-cache
頭,表示客戶端到服務端的節點無需重新驗證緩存是否過期而是強制要求服務端返回一個非緩存的版本(即使資源未發生變化,也不返回304 Not Modified
,而是返回200的正常響應):
如果覺得我的文章對您有幫助,歡迎關注我(CSDN:Mars Loo的博客)或者爲這篇文章點贊,謝謝!
如下幾篇文章是我在學習HTTP協議時總結的博文,歡迎參考:
1. HTTP代理及重定向
2. HTTP認證(基本認證與摘要認證)
3. HTTP、HTTPS基本原理