目錄
Last-Modify/If-Modify-Since——最後修改時間
一、HTTP緩存機制
Web 緩存大致可以分爲:數據庫緩存、服務器端緩存(代理服務器緩存、CDN 緩存)、瀏覽器緩存。
瀏覽器緩存也包含很多內容: HTTP 緩存、indexDB、cookie、localstorage 等等。
在具體瞭解 HTTP 緩存之前先來明確幾個術語:
- 緩存命中率:從緩存中得到數據的請求數與所有請求數的比率。理想狀態是越高越好。
- 過期內容:超過設置的有效時間,被標記爲“陳舊”的內容。通常過期內容不能用於回覆客戶端的請求,必須重新向源服務器請求新的內容或者驗證緩存的內容是否仍然準備。
- 驗證:驗證緩存中的過期內容是否仍然有效,驗證通過的話刷新過期時間。
- 失效:失效就是把內容從緩存中移除。當內容發生改變時就必須移除失效的內容。
瀏覽器緩存主要是 HTTP 協議定義的緩存機制。HTML meta 標籤,例如
<META HTTP-EQUIV="Pragma" CONTENT="no-store">
含義是讓瀏覽器不緩存當前頁面。但是代理服務器不解析 HTML 內容,一般應用廣泛的是用 HTTP 頭信息控制緩存。
Http緩存主要涉及三個角色:一是瀏覽器,二是瀏覽器的緩存數據庫,三是服務器。三者之間的圖示關係如下:
二、HTTP緩存的類型
Http緩存主要分爲兩種:強緩存和協商緩存。兩種緩存分別通過Http報文頭部不同的字段進行控制,下面將分別介紹兩種緩存。
1、強緩存(本地緩存)——不與服務器通信
命中強緩存時,瀏覽器並不會將請求發送給服務器。在Chrome的開發者工具中看到http的返回碼是200,但是在Size列會顯示爲(from *** cache)。
強緩存是利用http的返回頭中的Expires或者Cache-Control兩個字段來控制的,用來表示資源的緩存時間。
Expire——使用客戶端絕對時間
Expire其指定了一個日期/時間, 在這個日期/時間之後,HTTP響應被認爲是過時的。
該字段會返回一個時間,比如Expires:Thu, 27 Feb 2020 13:41:02 GMT。這個時間代表着這個資源的失效時間,也就是說在這個時間之前都是有效的,即命中緩存。這種方式有一個明顯的缺點,由於失效時間是一個絕對時間,所以當客戶端本地時間被修改以後,服務器與客戶端時間偏差變大以後,就會導致緩存混亂。於是發展出了Cache-Control。
相比Expire,cache-control的優先級更高,所以如果請求中還有一個置了 “max-age” 或者 “s-max-age” 指令的Cache-Control響應頭,那麼 Expires 頭就會被忽略。
Cache-Control——使用客戶端與服務器相對時間
Cache-Control是一個相對時間,例如Cache-Control:3600,代表着資源的有效期是3600秒。由於是相對時間,並且都是與客戶端時間比較,所以服務器與客戶端時間偏差也不會導致問題。
Cache-Control與Expires可以在服務端配置同時啓用或者啓用任意一個,同時啓用的時候Cache-Control優先級高。
Cache-Control 可以由多個字段組合而成,主要有以下幾個取值:
max-age:
指定一個時間長度,在這個時間段內緩存是有效的,單位是s。例如設置 Cache-Control:max-age=31536000,也就是說緩存有效期爲(31536000 / 24 / 60 * 60)天,第一次訪問這個資源的時候,服務器端也返回了 Expires 字段,並且過期時間是一年後。
在沒有禁用緩存並且沒有超過有效時間的情況下,再次訪問這個資源就命中了緩存,不會向服務器請求資源而是直接從瀏覽器緩存中取。
s-maxage:同 max-age,覆蓋 max-age、Expires,但僅適用於共享緩存,在私有緩存中被忽略。
public:表明響應可以被任何對象(發送請求的客戶端、代理服務器等等)緩存。
private:表明響應只能被單個用戶(可能是操作系統用戶、瀏覽器用戶)緩存,是非共享的,不能被代理服務器緩存。
no-cache:強制所有緩存了該響應的用戶,在使用已緩存的數據前,發送帶驗證器的請求到服務器。不是字面意思上的不緩存。
no-store:禁止緩存,每次請求都要向服務器重新獲取數據。
must-revalidate:指定如果頁面是過期的,則去服務器進行獲取。緩存必須在使用之前驗證舊資源的狀態,並且不可使用過期資源。這個指令並不常用,就不做過多的討論了。
2、協商緩存——與服務器通信
協商緩存就是由服務器來確定緩存資源是否可用,所以客戶端與服務器端要通過某種標識來進行通信,從而讓服務器判斷請求資源是否可以緩存訪問。
服務器根據http頭信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match來判斷是否命中協商緩存。如果命中,則http返回碼爲304,瀏覽器從緩存中加載資源。
Last-Modify/If-Modify-Since——最後修改時間
瀏覽器第一次請求一個資源的時候,服務器返回的header中會加上Last-Modify,Last-modify是一個時間標識該資源的最後修改時間,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。
當瀏覽器再次請求該資源時,發送的請求頭中會包含If-Modify-Since,該值爲緩存之前返回的Last-Modify。服務器收到If-Modify-Since後,根據資源的最後修改時間判斷是否命中緩存。
如果命中緩存,則返回http304,並且不會返回資源內容,並且不會返回Last-Modify。由於對比的服務端時間,所以客戶端與服務端時間差距不會導致問題。但是有時候通過最後修改時間來判斷資源是否修改還是不太準確(資源變化了最後修改時間也可以一致)。於是出現了ETag/If-None-Match。
ETag/If-None-Match——唯一標識符
與Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一個校驗碼(ETag: entity tag)。ETag可以保證每一個資源是唯一的,資源變化都會導致ETag變化。ETag值的變更則說明資源狀態已經被修改。服務器根據瀏覽器上發送的If-None-Match值來判斷是否命中緩存。
ETag擴展說明
我們對ETag寄予厚望,希望它對於每一個url生成唯一的值,資源變化時ETag也發生變化。神祕的Etag是如何生成的呢?以Apache爲例,ETag生成靠以下幾種因子
- 文件的i-node編號,此i-node非彼iNode。是Linux/Unix用來識別文件的編號。是的,識別文件用的不是文件名。使用命令’ls –I’可以看到。
- 文件最後修改時間
- 文件大小,生成Etag的時候,可以使用其中一種或幾種因子,使用抗碰撞散列函數來生成。所以,理論上ETag也是會重複的,只是概率小到可以忽略。
既生Last-Modified何生Etag?
你可能會覺得使用Last-Modified已經足以讓瀏覽器知道本地的緩存副本是否足夠新,爲什麼還需要Etag(實體標識)呢?HTTP1.1中Etag的出現主要是爲了解決幾個Last-Modified比較難解決的問題:
1. Last-Modified標註的最後修改只能精確到秒級,如果某些文件在1秒鐘以內,被修改多次的話,它將不能準確標註文件的修改時間
2. 如果某些文件會被定期生成,當有時內容並沒有任何變化,但Last-Modified卻改變了,導致文件沒法使用緩存
3.有可能存在服務器沒有準確獲取文件修改時間,或者與代理服務器時間不一致等情形
Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的唯一標識符,能夠更加準確的控制緩存。Last-Modified與ETag是可以一起使用的,服務器會優先驗證ETag,一致的情況下,纔會繼續比對Last-Modified,最後才決定是否返回304。
參考博客:https://www.cnblogs.com/ranyonsue/p/8918908.html
三、HTTP緩存機制執行流程
在瀏覽器第一次發起請求時,本地無緩存,向web服務器發送請求,服務器起端響應請求,瀏覽器端緩存。過程如下:
在第一次請求時,服務器會將頁面最後修改時間通過Last-Modified
標識由服務器發送給客戶端,客戶端記錄修改時間;服務器還會生成一個Etag,併發送給客戶端。
瀏覽器後續再次進行請求時:
瀏覽器緩存主要分爲強強緩存(也稱本地緩存)和協商緩存(也稱弱緩存)。根據上圖,瀏覽器在第一次請求發生後,再次發送請求過程:
(1)瀏覽器請求某一資源時,會先獲取該資源緩存的header信息,然後根據header中的
Cache-Control
和Expires
來判斷是否過期。
(2)若沒過期則直接從緩存中獲取資源信息,包括緩存的header的信息,所以此次請求不會與服務器進行通信。這裏判斷是否過期,則是強緩存相關。後面會講
Cache-Control
和Expires
相關。如果顯示已過期,瀏覽器會向服務器端發送請求,這個請求會攜帶第一次請求返回的有關緩存的header字段信息,比如客戶端會通過
If-None-Match
頭將先前服務器端發送過來的Etag發送給服務器,服務會對比這個客戶端發過來的Etag是否與服務器的相同。
(3)若相同,就將
If-None-Match
的值設爲false,返回狀態304,客戶端繼續使用本地緩存,不解析服務器端發回來的數據。若不相同就將
If-None-Match
的值設爲true,返回狀態爲200,客戶端重新機械服務器端返回的數據。
(4)客戶端還會通過
If-Modified-Since
頭將先前服務器端發過來的最後修改時間戳發送給服務器,服務器端通過這個時間戳判斷客戶端的頁面是否是最新的,如果不是最新的,則返回最新的內容,如果是最新的,則返回304,客戶端繼續使用本地緩存。
四、改進緩存方案
1、MD5/hash緩存
通過不緩存html,爲靜態文件添加MD5或hash標識,解決瀏覽器無法跳過緩存過期時間主動感知文件變化問題。
2、CDN緩存
CDN是構建在網絡之上的內容分發網絡,依靠部署在各地的邊緣服務器,通過中心平臺的負載均衡、內容分發、調度等功能模塊,使用戶就近獲取所需內容,降低網絡擁塞、提高用戶訪問響應速度和命中率。
http緩存是瀏覽器端緩存,cdn是服務器端緩存。
CDN怎麼緩存?
和Http類似,客戶端請求數據時,先從本地緩存查找,如果被請求數據沒有過期,拿過來用,如果過期,就向CDN邊緣節點發起請求。CDN便會檢測被請求的數據是否過期,如果沒有過期,就返回數據給客戶端,如果過期,CDN再向源站發送請求獲取新數據。和買家買貨,賣家沒貨,賣家再進貨一個道理。