關於 http 緩存,這些知識點你可能都不懂

2019-10-22     日行四善
  • 在地址欄輸入 url 回車,強緩存什麼時候失效,什麼時候有效,你知道嗎?
  • disk cache 和 memory cache 是什麼?在哪裡出現?
  • 火狐、IE 的緩存處理策略和 Chrome 一樣嗎?
  • cache-control 的 public、private 你知道什麼時候使用嗎?
  • 你知道 ETag 可能也有不適用的時候嗎?
  • 你知道 http 頭中 Vary 和緩存有怎樣的關係嗎?

如果上面的問題你都懂了,那關掉這個頁面吧;如果你喜歡本文,點一個贊吧~

先小小的回顧下

http header 描述 強緩存 協商緩存 Pragma 老版本的本地緩存機制,http 1.0 及以下 Expires 在此時候之後,響應過期,時間是絕對時間,受本地時間影響 * Cache-Control 強緩存策略 Cache-Control: public, max-age=31536000, must-revalidate max-age是相對時間 * Last-Modified、If-Modified-Since 資源最後被更改的時間,精確到秒 * ETag、If-None-Match 資源的標識值,用來唯一的標識一個資源 * 處理優先級

在本地 Cache-Control > Expires,Pragma 在不支持 Cache-Control 時生效。

如果本地緩存過期,則要依靠協商緩存

ETag > Last-Modified

強緩存的 http 狀態碼是 200 OK

協商緩存的 http 狀態碼是 304 Not Modified

Cache-Control

  • public 表明響應可以被任何對象(包括:發送請求的客戶端,代理伺服器,等等)緩存。
  • private 表明響應只能被單個用戶緩存,不能作為共享緩存(即代理伺服器不能緩存它)。私有緩存可以緩存響應內容。
  • no-cache 即使有緩存也會向伺服器發請求。
  • no-store 讓客戶端不要把資源存在緩存。
  • max-age= 設置緩存存儲的最大周期,超過這個時間緩存被認為過期(單位秒)。與 Expires 相反,時間是相對於請求的時間。
  • s-maxage= 覆蓋 max-age 或者 Expires 頭,但是僅適用於共享緩存(比如各個代理),私有緩存會忽略

問題 關於 public 和 private 的區別

翻譯成中文:private 不允許代理緩存。舉個例子,ISP 服務商可以在你的客戶端和網際網路之間加上不可見的代理,這個代理會緩存網頁來降低帶寬,客戶端設置 cache-control: private 之後,可以指定 ISP 代理不允許緩存網頁,但是允許最後的接受者緩存。而使用 cache-control: public 的意思是說,誰都可以緩存哈,所以中間代理會緩存一份以減少帶寬降低費用。

如果是誰都可以訪問的內容,比如網站 logo,那就用 public 好了,反正也不會有關鍵數據泄露風險,儘量中間代理都給緩存上,減少帶寬。而如果是一個含有用戶信息的頁面,比如頁面含有我的用戶名,那這個頁面當然不是對誰都有用,因為不同的人要返回不同的用戶名嘛,這個時候 private 會適合一些,如果代理緩存了我的這個頁面,別的用戶訪問又會緩存別人的,這顯然不合理,而且你的個人私密數據也儘量不要被保存在不受信任的地方。

當然,所有不被表示為 public 的數據都應該被標識為 private,要不然數據會存儲在中間伺服器上,別人就有可能會訪問到這個數據。

禁止緩存

Cache-Control: no-cache, no-store, must-revalidate

緩存靜態資源

Cache-Control:public, max-age=86400

ETag、If-Match

ETag 和 If-None-Match 常被用來處理協商緩存。而 ETag 和 If-Match 可以 避免「空中碰撞」

ETag HTTP響應頭是資源的特定版本的標識符。這可以讓緩存更高效,並節省帶寬,因為如果內容沒有改變,Web伺服器不需要發送完整的響應。而如果內容發生了變化,使用 ETag 有助於防止資源的同時更新相互覆蓋(「空中碰撞」)。

當編輯 MDN 時,當前的 Wiki 內容被散列,並在響應中放入Etag:

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4
複製代碼

將更改保存到 Wiki 頁面(發布數據)時,POST 請求將包含有 ETag 值的 If-Match 頭來檢查是否為最新版本。

If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
複製代碼

如果哈希值不匹配,則意味著文檔已經被編輯,拋出 412 ( Precondition Failed) 前提條件失敗錯誤。

If-None-Match 是客戶端發送給伺服器時的請求頭,其值是伺服器返回給客戶端的 ETag,當 If-None-Match 和伺服器資源最新的 Etag 不同時,返回最新的資源及其 Etag。

Last-Modified、If-Modified-Since

Last-Modified、If-Modified-Since 是資源最後更改的時間。

Last-Modified: ,    :: GMT
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
複製代碼

這兩個的區別是: Last-Modified 是伺服器發送給客戶端的,If-Modified-Since 是客戶端發送給伺服器的。

問題 Last-Modified 機制和 ETag 機制的區別和優先級是怎樣的?

Last-Modified 只能精確到秒,所以在秒級以下的更改無法檢測到。而 ETag 可以表徵文件的任何更改,只要文件變化 ETag 就會變化。所以 Last-Modified 是一個備用機制,優先級不如 Etag。

客戶端請求帶有 If-None-Match 在服務端校驗匹配則返回 304,校驗不匹配則返回 200,同時返回最新的資源和 Etag。

Age

Age 消息頭裡包含消息對象在緩存代理中存貯的時長,以秒為單位。

Age 消息頭的值通常接近於0。表示此消息對象剛剛從原始伺服器獲取不久;其他的值則是表示代理伺服器當前的系統時間與此應答消息中的通用消息頭 Date 的值之差。

Age: 
複製代碼

age: 1135860
複製代碼

Date

Date 是一個通用首部,其中包含了報文創建的日期和時間。

指的是響應生成的時間. 請求經過代理伺服器時, 返回的 Date 未必是最新的, 通常這個時候, 代理伺服器將增加一個 Age 欄位告知該資源已緩存了多久.

Date: Wed, 21 Oct 2015 07:28:00 GMT 
複製代碼

Vary

Vary 是一個HTTP響應頭部信息,它決定了對於未來的一個請求頭,應該用一個緩存的回覆(response)還是向源伺服器請求一個新的回覆。它被伺服器用來表明在 content negotiation algorithm(內容協商算法)中選擇一個資源代表的時候應該使用哪些頭部信息(headers)。

對於伺服器而言, 資源文件可能不止一個版本, 比如說壓縮和未壓縮, 針對不同的客戶端, 通常需要返回不同的資源版本。 比如說老式的瀏覽器可能不支持解壓縮, 這個時候, 就需要返回一個未壓縮的版本; 對於新的瀏覽器, 支持壓縮, 返回一個壓縮的版本, 有利於節省帶寬, 提升體驗. 那麼怎麼區分這個版本呢, 這個時候就需要 Vary 了。

伺服器通過指定 Vary: Accept-Encoding, 告知代理伺服器, 對於這個資源, 需要緩存兩個版本: 壓縮和未壓縮. 這樣老式瀏覽器和新的瀏覽器, 通過代理, 就分別拿到了未壓縮和壓縮版本的資源, 避免了都拿同一個資源的尷尬。

Vary: Accept-Encoding,User-Agent
複製代碼

如上設置, 代理伺服器將針對是否壓縮和瀏覽器類型兩個維度去緩存資源. 如此一來, 同一個url, 就能針對 PC 和 Mobile 返回不同的緩存內容。

怎麼讓瀏覽器不緩存靜態資源

可以設置 Cache-Control

Cache-Control: no-cache, no-store, must-revalidate
複製代碼

也可以給資源增加版本號,這樣可以很方便地控制什麼時候加載最新資源,這也是目前做版本更新比較常用的手段,即使老資源還在有效期內,加上了 query、hash。


複製代碼

用戶行為與緩存

用戶按 f5(ctrl+r)、ctrl+f5、點擊前進後退 都會觸發緩存機制

經過本地測試發現和網上傳的有些出入,記錄如下(強緩存有效代表直接使用本地的緩存文件,Chrome 狀態碼為 200,協商緩存有效代表向伺服器發起請求,伺服器可能返回 304 無內容或者 200 有內容)。

操作 強緩存 協商緩存 頁面連結跳轉 有效 有效 新開窗口 有效 有效 前進後退 有效 有效 地址欄回車 失效 或者 有效 有效 ctrl+r或者 f5 失效 有效 ctrl+f5 強制刷新 失效 失效

地址欄回車和網絡上不一樣,打個比方,如果當前已經在 http://localhost:3333/,然後在地址欄選中後回車,你會發現沒有緩存。

但是如果當前不在 http://localhost:3333/ ,比如 http://localhost:3333/index.css 或者空白頁,然後輸入 http://localhost:3333/ 回車,這時候就會直接從本地緩存中讀取。

驚喜不,意外不

關於 memory cache 和 disk cache

這兩種緩存類型存在於 Chrome 中。

上個小標題 用戶行為與緩存,我們看到瀏覽器從本地讀緩存的時候有一個 disk cache,與之對應的還有一個 memory cache。看名字也能大概才出來,disk cache 是從硬碟中讀取的文件緩存,memory cache 是從內存中直接讀取的內容,速度上當然也是後者更快。

那為什麼要有這兩種緩存的形式呢?

disk cache 存在硬碟,可以存很多,容量上限比內容緩存高很多,而 memory cache 從內存直接讀取,速度上占優勢,這兩個各有各的好處!

問題 瀏覽器如何決策使用哪種緩存呢?

來自知乎 瀏覽器是根據什麼決定「from disk cache」與「from memory cache」?

劃重點!!關於 Chrome、FF、IE 的緩存區別

這個內容網絡上很少有文章介紹,經過我測試之後發現區別挺大的。Chome 的緩存處理和另外兩個存在明顯的不同!

上面講的強緩存、memory cache、disk cache 在 FF、IE 中都沒有明顯的區分,或許這就是 Chrome 速度快的原因?

我們舉個例子,多次重複請求 https://www.baidu.com/,查看三個瀏覽器的區別。

Chrome

FF

IE

Chrome 中有強緩存、協商緩存,強緩存分為 memory cache、disk cache,這種處理機制可以最大化的利用緩存,減少發起的請求,從而優化頁面加載速度!!Chrome 資源狀態碼都是 200,沒有 304,另外兩家都存在大量304,在 FF、IE 中,即使伺服器明確表示資源需要進行強緩存,比如 Cache-Control: max-age=86400,他們仍然不會應用所謂的強緩存策略,仍然會向伺服器發送請求,伺服器返回 304 ,告訴瀏覽器用你本地的資源就好了!!!

怎麼知道這個請求確實是發送到了代理或者真實伺服器呢?我們上面有說 Age 和 Date,Age 表示當前請求在返回時,在代理那裡的時間減去 Date 的時間,所以每次只要請求發出去了,Age 都會相比上一次增加!!!

我們以掘金的 https://b-gold-cdn.xitu.io/v3/static/js/0.81b469df7a0dd87832a4.js 文件為例。在 FF 上,前一次的結果是

刷一下

Date 沒有變,表示都是使用的同一個真實伺服器的響應資源,Age 後一次比前一次變大了,但是狀態碼都是 304,而緩存規則是 cache-control: s-maxage=2592199, max-age=2592199!!!

所以想要做好緩存,需要考慮瀏覽器兼容的問題,綜合使用 http headers。

既然 Etag 可以校驗資源是否更改,那為什麼還要 Last-Modified 作為備用策略

這個問題大多數講緩存的文章也沒有提及。

Etag 是通過資源內容生成的,所以會有一個計算成本存在,本如大圖片的更改,它的最後更改時間可以很容易獲得,但是計算 Etag 成本就會高很多了。

原文連結:https://juejin.im/post/5da7286de51d4524a21c45d1

文章來源: https://twgreatdaily.com/zh/OuCI-G0BMH2_cNUgII6q.html