詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

這篇文章我們來聊一聊靜態資源的緩存策略,如果你準備去做頁面的優化,那麼這個知識點你就必須得了解。

首先明確一下靜態資源的概念,靜態可以理解為不變的。頁面中像js、css、img等文件都是靜態文件,因為此類文件你上線什麼內容,所有的用戶都會獲取一樣的內容,它是不會變化的,基於這樣的特點,這樣的文件可以做緩存,提高加載速度。

像一般的html文件,在你開發完還需要套各類模板,因為數據不同,展現的內容也不同,最典型的就是頭條的頁面,千人千面,每個頁面都不一樣,你很難從整體上做緩存,所以這樣的文件不能算是靜態資源。

詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

圖1

回想一下,在你開發完一個項目上線時,一般都是先上靜態資源後上模板,二者的上線方式會有所不同。靜態資源一般都會推到cdn服務器上去,而模板都是上到後端服務的機器上,前者就是想利用cdn的緩存策略讓用戶有更好的頁面加載體驗。

強緩存

1、瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在響應頭中加上Expires,如:

詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

圖2

2、瀏覽器在接收到這個資源後,會把這個資源連同所有響應頭一起緩存下來;

3、瀏覽器再請求這個資源時,先從緩存中尋找,找到這個資源後,拿出它的Expires跟當前的請求時間比較,如果請求時間在Expires指定的時間之前,就能命中緩存,否則就不行;

4、如果緩存沒有命中,瀏覽器會將請求發往服務器,Expires Header在重新加載的時候會被更新。

Expires是較老的強緩存管理header,由於它是服務器返回的一個絕對時間,在服務器時間與客戶端時間相差較大時,緩存管理容易出現問題,比如隨意修改下客戶端時間,就能影響緩存命中的結果。所以在http1.1的時候,提出了一個新的header,就是Cache-Control,這是一個相對時間,在配置緩存的時候,以秒為單位,用數值表示,如:Cache-Control:max-age=315360000,它的緩存原理如下:

1、瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在響應頭加上Cache-Control的header,如:

詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

圖3

2、瀏覽器在接收到這個資源後,會把這個資源連同所有響應頭一起緩存下來;

3、瀏覽器再請求這個資源時,先從緩存中尋找,找到這個資源後,根據它第一次的請求時間和Cache-Control設定的有效期,計算出一個資源過期時間,再拿這個過期時間跟當前的請求時間比較,如果請求時間在過期時間之前,就能命中緩存,否則就不行。

4、如果緩存沒有命中,瀏覽器直接從服務器加載資源時,Cache-Control Header在重新加載的時候會被更新。

Cache-Control描述的是一個相對時間,在進行緩存命中的時候,都是利用客戶端時間進行判斷,所以相比較Expires,Cache-Control的緩存管理更有效,安全一些。這兩個header可以只啟用一個,也可以同時啟用,當響應頭中,Expires和Cache-Control同時存在時,Cache-Control優先級高於Expires。

協商緩存

如果沒有命中強緩存,請求就會來到服務器,服務器檢查HTTP請求頭是否包含緩存驗證信息,如果協商緩存命中,請求響應返回的http狀態為304並且會顯示一個Not Modified的字符串。協商緩存利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】這兩對Header來管理的,原理如下:

1、瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在響應頭加上Last-Modified的header,這個header表示這個資源在服務器上的最後修改時間;

詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

圖4

2、瀏覽器再次跟服務器請求這個資源時,在請求頭上加上If-Modified-Since的header,這個header的值就是上一次請求時返回的Last-Modified的值;

詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

圖5

3、服務器再次收到資源請求時,根據瀏覽器傳過來If-Modified-Since和資源在服務器上的最後修改時間作對比判斷資源是否有變化,如果沒有變化則返回304 Not Modified,但是不會返回資源內容。如果有變化,就正常返回資源內容。當服務器返回304 Not Modified的響應時,response header中不會再添加Last-Modified的header,因為既然資源沒有變化,那麼Last-Modified也就不會改變。

4、瀏覽器收到304的響應後,就會從緩存中加載資源。

5、如果協商緩存沒有命中,瀏覽器直接從服務器加載資源時,Last-Modified Header在重新加載的時候會被更新,下次請求時,If-Modified-Since會啟用上次返回的Last-Modified值。

【Last-Modified,If-Modified-Since】都是根據服務器時間返回的header,一般來說,在沒有調整服務器時間和篡改客戶端緩存的情況下,這兩個header配合起來管理協商緩存是非常可靠的,但是有時候服務器上資源其實有變化,但是最後修改時間卻沒有變化的情況,而這種問題又很不容易被定位出來,而當這種情況出現的時候,就會影響協商緩存的可靠性。所以就有了另外一對header來管理協商緩存,這對header就是【ETag、If-None-Match】,它們的緩存管理的方式如下:

1、瀏覽器第一次跟服務器請求一個資源,服務器在返回這個資源的同時,在響應頭中加上ETag的header,這個header是服務器根據當前請求的資源生成的一個唯一標識,這個唯一標識是一個字符串,只要資源有變化這個串就不同,跟最後修改時間沒有關係,所以能很好的補充Last-Modified的問題;

詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

圖6

2、瀏覽器再次跟服務器請求這個資源時,在請求頭上加上If-None-Match的header,這個header的值就是上一次請求時返回的ETag的值:

詳解頁面靜態資源的緩存策略,搞懂強緩存和協商緩存再做性能優化

圖7

3、服務器再次收到資源請求時,根據瀏覽器傳過來的If-None-Match和再根據資源生成一個新的ETag,如果這兩個值相同就說明資源沒有變化,否則就是有變化;如果沒有變化則返回304 Not Modified,但是不會返回資源內容;如果有變化,就正常返回資源內容。與Last-Modified不一樣的是,當服務器返回304 Not Modified的響應時,由於ETag重新生成過,響應頭中還會把這個ETag返回,即使這個ETag跟之前的沒有變化。

4、瀏覽器收到304的響應後,就會從緩存中加載資源。

【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】一般都是同時啟用,這是為了處理Last-Modified不可靠的情況。有一種場景需要注意:

  • 分佈式系統裡多臺機器間文件的Last-Modified必須保持一致,以免負載均衡到不同機器導致比對失敗;
  • 分佈式系統儘量關閉掉ETag(每臺機器生成的ETag都會不一樣);

總結

這篇文章主要介紹了兩種緩存策略——強緩存和協商緩存,其實它是從瀏覽器地址欄輸入url到顯示頁面過程中的一部分。幾年之前,我在我的博客中就已經詳細介紹過了,這次再拿出來說,一是為了分享給更多的同學,二是為了下一篇開發一個靜態請求處理的node服務中間件做理論支撐。

喜歡我的文章就關注我吧,有問題可以發表評論,我們一起學習,共同成長!


分享到:


相關文章: