HTTP Cache 淺析

meils發表於2019-03-18

本文轉載至

深入理解瀏覽器的快取機制

HTTP Cache 為什麼讓人很困惑

檢視栗子:

基於http協議講解快取頭Cache-Control在服務中的應用

HTTP Cache 淺析

一、Private browser caches 和 Shared proxy caches

緩衝這塊並不怎麼好理解,網上查閱的資料,都講的比較模糊,就比如:Private browser cachesShared proxy caches

瀏覽器快取的服務架構可能是這樣的:瀏覽器(Cache)<=>伺服器。
代理伺服器快取架構可能是這樣的:瀏覽器 <=> CDN(Cache)<=>源伺服器。
複製程式碼

不同的 HTTP Cache 解決的問題和使用的場景是不一樣的。個人理解瀏覽器快取主要是為了避免不必要的請求和大量的網路傳輸,而代理伺服器快取主要是為了讓服務離使用者更近更有效率(當然也解決了請求和網路傳輸)。

對於 Web 開發者來說,可能經常遇到的還是瀏覽器快取,當我們訪問一個網站的時候,會進行緩衝,當我們下一次訪問的時候,可能看到的就是緩衝中載入的頁面。這樣可以大大加快訪問速度。

二、正確理解 Cache-Control 指令

這個指令是一個通用首部欄位,就是說這個指令能夠作為請求和響應指令,同時這個指令的引數也有多個,比如說其引數 max-age = 0 在請求和響應指令中分別代表什麼?在理解的時候一定要分辨清楚。

三、進一步理解 Cache-Control 指令

它有三個含義:

(1)能否快取(針對響應來說)

private:表示它只應該存在與瀏覽器快取。
public:表示它可以快取在瀏覽器或者 CDN 上。
no-cache:這個詞很迷惑,不是代表不能使用快取,而是代表在使用前必須到伺服器上確認。
no-store:表示不允許被快取。
複製程式碼

(2)快取多久(針對響應來說)

max-age= 秒,告知瀏覽器這個快取的有效時間多少。
複製程式碼

(3)revalidation(針對響應來說,就是條件檢查)

must-revalidate:
表示瀏覽器必須檢查伺服器,確認本地快取是否有效,這個引數和請求引數 max-age = 0 有些類似。
複製程式碼

這個指令形象的告訴瀏覽器,你是不是可以快取這個物件,這個物件快取時間是多少,是否在每次使用快取的時候先確認下。

一張圖描述的很清晰:

HTTP Cache 淺析

  • 這個資源是否允許快取?
  • 客戶端每次使用快取的時候需要去伺服器校驗嗎?
  • 這個快取是 Public 的還是 Private?
  • 快取時間多少?
  • 資源識別符號是什麼(Etag)?

HTTP Cache 淺析

四、Expires和Cache-Control兩者對比

其實這兩者差別不大,區別就在於 Expires 是http1.0的產物,Cache-Control是http1.1的產物,兩者同時存在的話,Cache-Control優先順序高於Expires;在某些不支援HTTP1.1的環境下,Expires就會發揮用處。所以Expires其實是過時的產物,現階段它的存在只是一種相容性的寫法。

五、協商緩衝

協商快取就是強制快取失效後,瀏覽器攜帶快取標識向伺服器發起請求,由伺服器根據快取標識決定是否使用快取的過程,主要有以下兩種情況:

  • 協商快取生效,返回304和Not Modified

HTTP Cache 淺析

  • 協商快取生效 ,返回200和請求結果

HTTP Cache 淺析

協商快取可以通過設定兩種 HTTP Header 實現:Last-Modified 和 ETag 。

1.Last-Modified和If-Modified-Since

瀏覽器在第一次訪問資源時,伺服器返回資源的同時,在response header中新增 Last-Modified的header,值是這個資源在伺服器上的最後修改時間,瀏覽器接收後快取檔案和header

Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
複製程式碼

瀏覽器下一次請求這個資源,瀏覽器檢測到有 Last-Modified這個header,於是新增If-Modified-Since這個header,值就是Last-Modified中的值;伺服器再次收到這個資源請求,會根據 If-Modified-Since 中的值與伺服器中這個資源的最後修改時間對比,如果沒有變化,返回304和空的響應體,直接從快取讀取,如果If-Modified-Since的時間小於伺服器中這個資源的最後修改時間,說明檔案有更新,於是返回新的資原始檔和200

HTTP Cache 淺析

但是 Last-Modified 存在一些弊端:

如果本地開啟快取檔案,即使沒有對檔案進行修改,但還是會造成 Last-Modified 被修改,服務端不能命中快取導致傳送相同的資源 因為 Last-Modified 只能以秒計時,如果在不可感知的時間內修改完成檔案,那麼服務端會認為資源還是命中了,不會返回正確的資源

既然根據檔案修改時間來決定是否快取尚有不足,能否可以直接根據檔案內容是否修改來決定快取策略?所以在 HTTP / 1.1 出現了 ETagIf-None-Match

2.ETag和If-None-Match

Etag是伺服器響應請求時,返回當前資原始檔的一個唯一標識(由伺服器生成),只要資源有變化,Etag就會重新生成。瀏覽器在下一次載入資源向伺服器傳送請求時,會將上一次返回的Etag值放到request header裡的If-None-Match裡,伺服器只需要比較客戶端傳來的If-None-Match跟自己伺服器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。如果伺服器發現ETag匹配不上,那麼直接以常規GET 200回包形式將新的資源(當然也包括了新的ETag)發給客戶端;如果ETag是一致的,則直接返回304知會客戶端直接使用本地快取即可。

HTTP Cache 淺析

3.兩者之間對比:

  • 首先在精確度上,Etag要優於Last-Modified。

Last-Modified的時間單位是秒,如果某個檔案在1秒內改變了多次,那麼他們的Last-Modified其實並沒有體現出來修改,但是Etag每次都會改變確保了精度;如果是負載均衡的伺服器,各個伺服器生成的Last-Modified也有可能不一致。

  • 第二在效能上,Etag要遜於Last-Modified,畢竟Last-Modified只需要記錄時間,而Etag需要伺服器通過演算法來計算出一個hash值。
  • 第三在優先順序上,伺服器校驗優先考慮Etag

六、快取機制

通常瀏覽器快取策略分為兩種:強快取和協商快取。如果快取過期了,我們就可以使用協商快取來解決問題。協商快取需要請求,如果快取有效會返回 304。

HTTP Cache 淺析

七、實際場景應用快取策略

頻繁變動的資源

Cache-Control: no-cache
複製程式碼

對於頻繁變動的資源,首先需要使用Cache-Control: no-cache 使瀏覽器每次都請求伺服器,然後配合 ETag 或者 Last-Modified 來驗證資源是否有效。這樣的做法雖然不能節省請求數量,但是能顯著減少響應資料大小。

不常變化的資源

Cache-Control: max-age=31536000
複製程式碼

通常在處理這類資源時,給它們的 Cache-Control 配置一個很大的max-age=31536000 (一年),這樣瀏覽器之後請求相同的 URL 會命中強制快取。

而為了解決更新的問題,就需要在檔名(或者路徑)中新增hash, 版本號等動態字元,之後更改動態字元,從而達到更改引用 URL 的目的,讓之前的強制快取失效 (其實並未立即失效,只是不再使用了而已)。 線上提供的類庫 (如 jquery-3.3.1.min.js, lodash.min.js 等) 均採用這個模式。

八、使用者行為對瀏覽器快取的影響

所謂使用者行為對瀏覽器快取的影響,指的就是使用者在瀏覽器如何操作時,會觸發怎樣的快取策略。主要有 3 種:

  • 開啟網頁,位址列輸入地址: 查詢 disk cache 中是否有匹配。如有則使用;如沒有則傳送網路請求。

  • 普通重新整理 (F5):因為 TAB 並沒有關閉,因此 memory cache 是可用的,會被優先使用(如果匹配的話)。其次才是 disk cache。

  • 強制重新整理 (Ctrl + F5):瀏覽器不使用快取,因此傳送的請求頭部均帶有 Cache-control: no-cache(為了相容,還帶了 Pragma: no-cache),伺服器直接返回 200 和最新內容。