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