什么是http緩存呢,當我們使用chrome瀏覽器,按F12打開控制台,在網絡請求中有時候看到狀態碼是200,有時候狀態碼是304,當我們去看這種請求的時候,我們會發現狀態碼為304的狀態結果是:Status Code: 304 Not Modified,而狀態碼為200的時候一般會有四種情況,一種是直接返回200,沒有任何其他的標志,另一種是Status Code: 200 OK (from memory cache),還有一種是Status Code: 200 (from disk cache)。最后一種不是太常見,Status Code: 200 (from Service Worker).后面這三種狀態碼看到的效果是灰色的,其實從給出的信息也能看出來是從緩存中獲取上數據。下面我們來詳細介紹一下他們都分別是什么時候出現的。
其實我們可以按狀態碼來區分其為兩大類,分別是協商緩存--304和強制緩存--200
協商緩存(304)
這種方式使用到了headers請求頭里的兩個字段,Last-Modified & If-Modified-Since 。服務器通過響應頭Last-Modified告知瀏覽器,資源最后被修改的時間:
Last-Modified: Thu, 20 Jun 2019 15:58:05 GMT
當再次請求該資源時,瀏覽器需要再次向服務器確認,資源是否過期,其中的憑證就是請求頭If-Modified-Since字段,值為上次請求中響應頭Last-Modified字段的值:
If-Modified-Since: Thu, 20 Jun 2019 15:58:05 GMT
瀏覽器在發送請求的時候服務器會檢查請求頭request header里面的If-modified-Since,如果最后修改時間相同則返回304,否則給返回頭(response header)添加last-Modified並且返回數據(response body)。
另外,瀏覽器在發送請求的時候服務器會檢查請求頭(request header)里面的if-none-match的值與當前文件的內容通過hash算法(例如 nodejs: cryto.createHash('sha1'))生成的內容摘要字符對比,相同則直接返回304,否則給返回頭(response header)添加etag屬性為當前的內容摘要字符,並且返回內容。
綜上總結為:
- 請求頭last-modified的日期與響應頭的last-modified一致
- 請求頭if-none-match的hash與響應頭的etag一致
這兩種情況會返回Status Code: 304
強制緩存(200(from ...))
這里我們使用到了Cache-Control和Expires這兩個字段來進行控制,Cache-Control里面可以有多個屬性,可以組合設置,其屬性有如下幾種:
- max-age=xxx,最大的有效時間 單位秒,是一個相對時間
- must-revalidate,如果超過了max-age的時間,必須向服務器發送請求,驗證資源的有效性
- no-cache,基本等價於max-age=0,由協商緩存來決定是否緩存資源
- no-store,真正意義上的不緩存
- public,代表 http 請求返回的內容所經過的任何路徑當中(包括中間一些http代理服務器以及發出請求的客戶端瀏覽器),都可以對返回內容進行緩存操作
- private,代表只有發起請求的瀏覽器才可以進行緩存。默認值
比如我們設置
Catch-Control:public,max-age=360000
我們在之前說了強制緩存有三種情況,上面說返回200有四種,第一種其實是不緩存,正常服務器返回響應。
Service Worker
這個東西其實本質上時服務器和客戶端之間的代理服務器,一般我們在使用react開發的時候,會發現在根目錄出現了一個server-worker.js文件,這個東西就是和service Worker緩存相關的,他會根據網絡的狀態做出不同的緩存策略,有時候斷網了,之前訪問過的接口有可能依然會返回數據,其數據來源就是從其緩存中讀取。
memory cache
這個是將資源緩存在了內存中。事實上,所有的網絡請求都會被瀏覽器緩存到內存中,當然,內存容量有限,緩存不能無限存放在內存中,因此,注定是個短期緩存。而其控制權在於瀏覽器。
disk cache
與內存緩存相對的,這個是將資源緩存在硬盤中。雖然相比於內存,硬盤的讀取速度要慢很多,但總比沒有強。硬盤緩存的控制權在后端,通過什么控制呢?通過HTTP響應頭控制,也就是我們在上面說到的catche-control和expires
Expires設置的過期時間是一個絕對的GMT時間,例如:Expires:Thu,20 Jun 2019 20:00:00 GMT; 他告訴瀏覽器緩存有效性持續到2019年6月20日為止,一直都使用緩存來處理。
Expires有一個非常大的缺陷,它使用一個固定的時間,要求服務器與客戶端的時鍾保持嚴格的同步,並且這一天到來后,服務器還得重新設定新的時間。
HTTP1.1引入了Cathe-Control,它使用max-age指定組件被緩存多久,從請求開始在max-age時間內瀏覽器使用緩存,之外的使用請求,這樣就可以消除Expires的限制,
如果對瀏覽器兼容性要求很高的話,可以兩個都使用。
其實在上面說到的Last-Modified對比最后修改時間與Expires一樣是有缺陷的,如果,資源的變化的時間間隔小於秒級,比如說是毫秒級的,或者說資源直接是動態生成的,那根據Last-Modified判斷,資源就是每時每刻都最新的,即被修改過!
所以,Etag & If-Node-Match 就是來解決這個問題的。
Etag字段的值為文件的特殊標識,一般都是hash生成的,服務器存儲着資源的Etag值。接下來的流程都與Lst-Modified & If-Modified-Since一致,只不過,比較的值從最后修改時間變成了Etag值。
Etag的優點在於,對於動態資源或者現在流行的Restful API返回的JSON數據,這些是沒有修改時間這一說法的,但是Http標准並沒有規定Etag值如何生成,因此我們通過代碼自己生成Etag值。當然,計算Etag值會消耗服務器性能。
Cache-Control+Last-Modified+ETag 的優先級會如何?
因為http1.1>http1.0,所以Cache-Control>Expires,ETag>Last-Modified。依照就近原則,先找本地緩存,沒有再向服務器發請求,所以Expires>Last-Modified,Cache-Control>ETag,
如果瀏覽器只支持http1.0,那么瀏覽器只會攜帶Last-Modified發送給后台,
如果服務器只支持http1.0,那么服務器會以Last-Modified為標准。
如果瀏覽器支持http1.1,那么瀏覽器會攜帶Cache-Control+Last-Modified+ETag發送給后台,
如果服務器支持http1.1,那么服務器會以Cache-Control+ETag為標准。
200狀態碼和304狀態碼何時出現
在沒有設置Cache-Contral的情況下,設置Last-Modified和ETag緩存,會出現200(from cache)和304 交替出現的情況。
設置Cache-Contral的情況下,過期刷新會出現304(如果有更新內容,則是200),之后再過期之前刷新都是200(from cache)。如果要確保要向服務端確認,可以將Cache-Contral的max-age設置為0。
通過下圖我們可以清晰的明白200和304
https://www.oecom.cn/http-header-control/