從未如此簡單:5分鐘搞懂 HTTP 緩存機制
什么是 HTTP 緩存
HTTP 緩存可以說是HTTP性能優(yōu)化中簡單高效的一種優(yōu)化方式了,緩存是一種保存資源副本并在下次請求時直接使用該副本的技術(shù),當 web 緩存發(fā)現(xiàn)請求的資源已經(jīng)被存儲,它會攔截請求,返回該資源的拷貝,而不會去源服務器重新下載。
一個優(yōu)秀的緩存策略可以縮短網(wǎng)頁請求資源的距離,減少延遲,節(jié)省網(wǎng)絡流量,并且由于緩存文件可以重復利用,降低網(wǎng)絡負荷,提高客戶端響應。
所以,學會利用 HTTP 緩存是很有必要的。
在此,我會向大家系統(tǒng)的介紹HTTP緩存機制,期望對各位正確的理解HTTP緩存有所幫助。
緩存策略
在闡述HTTP不同緩存策略之前,我們需要知道用戶刷新/訪問行為 的手段分成三類:
- 在URI輸入欄中輸入然后回車/通過書簽訪問
- F5/點擊工具欄中的刷新按鈕/右鍵菜單重新加載
- Ctl+F5 (完全不使用HTTP緩存)
不同的刷新手段,會導致瀏覽器使用不同的緩存策略,我們下面會分析到
HTTP 緩存主要是通過請求和響應報文頭中的對應 Header 信息,來控制緩存的策略。
響應頭中相關字段為Expires、Cache-Control、Last-Modified、Etag。
HTTP緩存的類型很多,根據(jù)是否需要重新向服務器發(fā)起請求來分類包括兩種:強制緩存和對比緩存
假設瀏覽器有一個緩存數(shù)據(jù)庫用于本地緩存,先看看瀏覽器請求資源的情況:
強制緩存
在瀏覽器已經(jīng)緩存數(shù)據(jù)的情況下,使用強制緩存去請求數(shù)據(jù)的流程是這樣的:
從流程圖可以看到,強制緩存,在緩存數(shù)據(jù)未失效的情況下,可以直接使用緩存數(shù)據(jù),不需要再請求服務器,那么瀏覽器是如何判斷緩存數(shù)據(jù)是否失效呢?
對于強制緩存來說,響應header中會有兩個字段來標明失效規(guī)則(Expires/Cache-Control):
- Expires:
Expires 是 HTTP1.0 的產(chǎn)物了,現(xiàn)在默認瀏覽器均默認使用 HTTP 1.1,所以它的作用基本忽略。但是很多網(wǎng)站還是對它做了兼容。它的值為服務端返回的到期時間,即下一次請求時,請求時間小于服務端返回的到期時間,直接使用緩存數(shù)據(jù)。
但有一個問題是到期時間是由服務端生成的,如果客戶端時間跟服務器時間不一致,這就會導致緩存命中的誤差。
在 HTTP 1.1 的版本,Expires 被 Cache-Control 替代。
- Cache-Control:
Cache-Control 是最重要的規(guī)則。常見的取值有 private、public、no-cache、max-age,no-store,默認為 private。
1) max-age:用來設置資源(representations)可以被緩存多長時間,單位為秒;
2) s-maxage:和 max-age 是一樣的,不過它只針對代理服務器緩存而言;
3) public:指示響應可被任何緩存區(qū)緩存;
4) private:只能針對個人用戶,而不能被代理服務器緩存;
5) no-cache:強制客戶端直接向服務器發(fā)送請求,也就是說每次請求都必須向服務器發(fā)送。服務器接收到請求,然后判斷資源是否變更,是則返回新內(nèi)容,否則返回304,未變更。這個很容易讓人產(chǎn)生誤解,使人誤以為是響應不被緩存。實際上Cache-Control: no-cache是會被緩存的,只不過每次在向客戶端(瀏覽器)提供響應數(shù)據(jù)時,緩存都要向服務器評估緩存響應的有效性。
6) no-store:禁止一切緩存(這個才是響應不被緩存的意思)。
舉個例子,比如一個資源響應頭是:
- cache-control: public, max-age= 31536000
那么這個資源會被緩存31536000秒(365天),在365天內(nèi)再次請求這條數(shù)據(jù),都會直接獲取緩存數(shù)據(jù)庫中的數(shù)據(jù),直接使用。
那么我們試試再次訪問資源,會有以下的響應:
可以看到HTTP狀態(tài)碼是200,Size這個字段顯示:disk cache,說明瀏覽器確實走了強制緩存,沒有再跟瀏覽器交互。
我們上面說了,不同的訪問/刷新手段,會使瀏覽器使用不同的緩存策略,要讓瀏覽器走強制緩存對請求方式有一個要求: 在URI輸入欄中輸入然后回車/通過書簽訪問。
對比緩存
在瀏覽器已經(jīng)緩存數(shù)據(jù)的情況下,使用對比緩存去請求數(shù)據(jù)的流程是這樣的。
有同學可能會問,基于對比緩存的流程下,不管是否使用緩存,都需要向服務器發(fā)送請求,那么還用緩存干什么?
這個問題,我們現(xiàn)在來探討一下。
對比緩存,顧名思義,需要進行比較判斷是否可以使用緩存。
瀏覽器第一次請求數(shù)據(jù)時,服務器會將緩存標識與數(shù)據(jù)一起返回給瀏覽器,瀏覽器將二者備份至緩存數(shù)據(jù)庫中。
當瀏覽器再次請求數(shù)據(jù)時,瀏覽器將備份的緩存標識發(fā)送給服務器,服務器根據(jù)緩存標識進行判斷,判斷成功后,返回304狀態(tài)碼,通知客戶端比較成功,可以使用緩存數(shù)據(jù)。
舉個例子,第一次訪問:
第二次訪問:
對于對比緩存來說,響應 header 中會有兩個字段來標明規(guī)則
- Last-Modified / If-Modified-Since
服務器響應請求時,會告訴瀏覽器一個告訴瀏覽器資源的最后修改時間:Last-Modified,瀏覽器之后再請求的時候,會帶上一個頭:If-Modified-Since,這個值就是服務器上一次給的 Last-Modified 的時間,服務器會比對資源當前最后的修改時間,如果大于If-Modified-Since,則說明資源修改過了,瀏覽器不能再使用緩存,否則瀏覽器可以繼續(xù)使用緩存,并返回304狀態(tài)碼。
- Etag / If-None-Match(優(yōu)先級高于Last-Modified / If-Modified-Since)
服務器響應請求時,通過Etag頭部告訴瀏覽器當前資源在服務器的唯一標識(生成規(guī)則由服務器決定),瀏覽器再次請求時,就會帶上一個頭If-None-Match,這個值就是服務器上一次給的Etag的值,服務器比對一下資源當前的Etag是否跟If-None-Match一致,不一致則說明資源修改過了,瀏覽器不能再使用緩存,否則瀏覽器可以繼續(xù)使用緩存,并返回304狀態(tài)碼。
看個例子:第一次請求,服務器的響應頭包含了:
第二次請求,瀏覽器的請求頭
總結(jié)
我們再看一下HTTP緩存的一個總概流程圖:
- HTTP緩存主要分強制緩存和對比緩存
- 強制緩存的 HTTP 相關頭部 Cache-Control,Exipres(HTTP1.0),瀏覽器直接讀本地緩存,不會再跟服務器端交互,狀態(tài)碼 200。
- 對比緩存的 HTTP 相關頭部 Last-Modified / If-Modified-Since, Etag / If-None-Match (優(yōu)先級比Last-Modified / If-Modified-Since高),每次請求需要讓服務器判斷一下資源是否更新過,從而決定瀏覽器是否使用緩存,如果是,則返回304,否則重新完整響應。