前端HTTP緩存簡單瞭解

NO IMAGE

HTTP 緩存簡單瞭解。文章整理了相關資料,記錄了部分實踐。方便大家輕鬆瞭解緩存。能回答上三個問題,HTTP緩存就算理解呢。能否緩存?緩存是否過期?協商緩存?

概要:

  • web緩存
  • 緩存的處理
  • 前端解決方案
  • 總結

1. web緩存

Web緩存是可以自動保存常見文檔副本的 HTTP 設備。當 Web請求抵達緩存時, 如果本地有“已緩存的”副本,就可以從本地存儲設備而不是原始服務器中提取這個文檔。《HTTP權威指南》

緩存是一種存儲給定資源副本並在請求時將其提供回來的技術。

當Web緩存在其存儲中具有請求的資源且能用時,它將攔截該請求並返回其副本,而不是從原始服務器重新下載。

關鍵字:緩存,原始服務器(產生原始文檔)

1.1 緩存類型

緩存的種類:瀏覽器緩存(本文討論點),代理緩存,網關緩存。

以上種類 緩存工作的原理是一致的,只是緩存所在的位置不同,涉及面更寬廣。

前端HTTP緩存簡單瞭解

這幾種緩存,可以分為兩大類:

  • 專用緩存(私有緩存):私有緩存專用於單個用戶。
  • 共享緩存:多用戶共享。

前端HTTP緩存簡單瞭解

圖片源:HTTP caching

上圖展示了:

沒有緩存:沒有緩存直接向服務器請求資源。

共享緩存:當用戶Browser1請求資源,經過緩存服務器,緩存服務器也沒有資源,向原始服務器請求資源。得到資源後,緩存服務器緩存資源並返還數據給Browser1。當用戶Browser2請求相同資源時,緩存服務器有資源,且能用,就直接返還數據給Browser2,不再向原始服務器發起請求。

私有緩存:用戶Browser1請求資源,向服務器請求資源。得到資源後,緩存在本地,供下一次請求同樣資源時判定使用。用戶Browser2需要同樣的資源,只能向服務器請求資源,並緩存供下一次請求同樣資源時判定使用。

1.2 緩存目的

緩存減少了冗餘的數據傳輸,節省了你的網絡費用。
緩存緩解了網絡瓶頸的問題。不需要更多的帶寬就能夠更快地加載頁面。
緩存降低了對原始服務器的要求。服務器可以更快地響應,避免過載的出現。
緩存降低了距離時延,因為從較遠的地方加載頁面會更慢一些。

2. 緩存的處理

對於HTTP 緩存流程中涉及到的簡單問題及相關首部字段。

前端HTTP緩存簡單瞭解

  • Http 響應的內容是否可緩存到客戶端(能否緩存)。
  • 客戶端是否可直接從本地緩存中加載並展示,或者發送請求到服務端再驗證(緩存是否過期)。
  • 客戶端將緩存標識發往服務端,服務端通過標識來判斷客戶端的緩存是否仍有效,或發送新的數據給客戶端(協商緩存能否再用)。

2.1 相關的首部字段

2.1.1 數據能否緩存,相關字段

⑴ 默認存儲

默認情況下,如果請求方法,請求標頭字段和響應狀態的要求表明響應是可緩存的,則該響應是可緩存的。

常見的HTTP緩存通常僅限於緩存對GET的響應,並且可能會拒絕其他方法。 主緩存鍵由請求方法和目標URI組成(通常僅使用URI,因為只有GET請求才是緩存目標)。

除非特別受cache-control指令約束,否則緩存系統可以始終將成功的響應存儲為緩存條目,如果新鮮則可以不經驗證就將其返回。如果新鮮也可以在成功驗證後返回。

狀態碼為200、203、206、300、301或410的響應也可以由緩存存儲,並用於回覆後續請求。

具體參考響應可緩存性

⑵ Cache-Control

Cache-Control頭裡的no-store、no-cache、Public、Private、max-age 用來指明響應內容是否可以被客戶端存儲,

no-store :禁止進行緩存緩存不應存儲有關客戶端請求或服務器響應的任何內容。每次由客戶端發起的請求都會下載完整的響應內容。
no-cache:緩存但重新驗證緩存將在使用緩存副本之前,將此請求(帶有與本地緩存相關的驗證字段)到原始服務器進行驗證。
public: 公共緩存表示該響應可以被任何緩存器(比如中間代理、CDN等)緩存
一些通常不被中間緩存器緩存的頁面(比如 帶有HTTP驗證信息(帳號密碼)的頁面 或 某些特定狀態碼的頁面),將會被其緩存。
s-maxage=<seconds>: 緩存有效時間同max-age作用一樣表示緩存有效時間,但 s-maxage指令只適用於供多位用戶使用的公共緩存服務器(比如CDN緩存)。使用 s-maxage 指令後,直接忽略對 Expires 首部字段及max-age 指令的處理。
private: 私有緩存表示該響應是專用於某單個用戶的,該響應只能應用於瀏覽器私有緩存中。
max-age=<seconds>: 緩存有效時間表示資源能夠被緩存(保持新鮮)的最大時間。相對Expires而言,max-age是距離請求發起的時間的秒數。

⑶ Expires:

指明可以被客戶端存儲,還告訴了時間。(Expires首部和Cache-control:max-age 首部做的事情基本一致)
Expires:value為緩存過期時間,用來指定資源到期的時間,是服務器端具體的時間點,在過期時間前瀏覽器可以直接使用緩存數據。如果響應中存在帶有max-age或s-maxage指令的Cache-Control標頭,則Expires標頭將被忽略。

2.1.2 緩存是否過期

⑴ 不能直接使用

Cache-Control:no-cache

緩存將在使用緩存副本之前,將此請求(帶有與本地緩存相關的驗證字段)到原始服務器進行驗證。

⑵ 計算是否過期

Date:創建報文的日期時間。

Expires:指明可以被客戶端存儲,還告訴了時間。(Expires首部和Cache-control:max-age 首部做的事情基本一致)。

Cache-Control:max-age=<seconds> 緩存有效時間。

計算新鮮度公式如下:

max-age指令優先於Expires,因此,如果響應中存在max-age,則計算很簡單:
// 新鮮度 = max_age_value
fresh_lifetime = max_age_value
否則,如果響應中存在Expires,則計算為:
// 新鮮度 = expires_value - date_value(Date創建報文的日期時間(啟發式緩存階段會用到這個字段))
fresh_lifetime = expires_value - date_value

Age:告訴接收端響應已產生多長時間(Age值有具體算法感興趣可以查看Age Calculations)(HTTP/1.1緩存必須在發送每條響應中都包含一個Age頭部)

緩存計算是否過期:

// 響應是否新鮮    current_age: 是瀏覽器計算出的age 值
response_is_fresh = (freshness_lifetime > current_age)

當響應中沒有Cache-Contral:max-age 首部,也沒有Expires首部,緩存可以計算出一個試探性最大使用期,即啟發式緩存。

⑶ 啟發式緩存:

如果響應中未顯示Expires,Cache-Control:max-age或Cache-Control:s-maxage,並且響應中不包含其他有關緩存的限制,緩存可以使用啟發式方法計算新鮮度壽命。

通常會根據響應頭中的2個時間字段 Date 減去 Last-Modified 值的 10% 作為緩存時間。

// Date 減去 Last-Modified 值的 10% 作為緩存時間。
// Date:創建報文的日期時間, Last-Modified 服務器聲明文檔最後被修改時間
response_is_fresh =  max(0,(Date -  Last-Modified)) % 10

通常會設置計算出來的值會設置上線。但服務器端最好還是顯示提供到期時間比較好

HTTP / 1.1規範沒有提供特定的算法,但是對結果施加了最壞情況的約束。 由於啟發式到期時間可能會損害語義透明性,因此應謹慎使用,並且我們鼓勵原始服務器儘可能提供顯式的到期時間。

當緩存文檔過期的時候,需要客戶端帶上緩存的標識去服務端驗證,緩存是否還能再用。這就是協商緩存過程了。

2.1.3 協商緩存能否再用相關字段

當客戶端第一次請求的時候沒有帶條件首部,服務端響應帶有條件首部,如Last-Modified ,ETag等,當下次緩存過期客戶端將緩存的數據標識發往服務端進行驗證。

前端HTTP緩存簡單瞭解

HTPP定義的條件首部最有用的兩個 If-Modified-Since 和If-None-Match

⑴ Last-Modified 和 If-Modified-Since
Last-Modified(服務器響應首部): 服務器記錄的資源的更新時間。
If-Modified-Since(請求首部字段):已緩存副本的最後修改日期。

當緩存過期,再驗證。客戶端將緩存的數據標識If-Modified-Since發往服務端,服務端將用Last-Modified 與 If-Modified-Since做對比。If-Modified-Since 字段值早於資源的Last-Modified更新時間,則希望返回新資源。而在指定 If-Modified-Since 字段值的日期時間之後,如果請求的資源都沒有過更新,則返回狀態碼 304 Not Modified 的響應。

前端HTTP緩存簡單瞭解

缺點:last-Modified 只能精確到秒,文件的修改非常頻繁,在秒以下的時間內進行修改,Last-Modified不能精確。

一個文件位於多個CDN服務器上內容雖然一樣,當修改時間不一樣。(比對後會返回信息更新)

所以在 HTTP / 1.1 出現了 ETag 。

⑵ ETag 和 If-None-Match

ETag(服務器響應首部): 實體標識。它是一種可將資源以字符串形式做唯一性標識的方式。服務器會為每份資源分配對應的 ETag值。

If-None-Match(請求首部字段):緩存的實體標籤。用於指定 If-None-Match 字段值的實體標記(ETag)值與請求資源的 ETag 不一致時,它就告知服務器處理該請求返回新資源,相反則返回狀態碼 304 Not Modified 的響應。

當緩存過期,再驗證。客戶端將緩存的數據實體標籤If-None-Match發往服務端,服務端將用If-None-Match 與 ETag做對比。

前端HTTP緩存簡單瞭解

⑶ 其它if 條件首部參考文檔

2.1.4 vary 可以簡單瞭解

vary可以簡單瞭解,後端用於配置。

vary定義如下:

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

舉個例子:圖片來源《圖解http》

前端HTTP緩存簡單瞭解

一個客戶端向服務器請求/sample.html資源,Accept-Language: en-us,代理服務器沒有此資源,向服務器請求

服務器返回了資源,HTTP響應頭部信息Vary指定了 Accept-Language,代理服務器返回資源給客戶端,並緩存了數據。

第二個客戶端向服務器請求/sample.html資源,Accept-Language: zh-cn,代理服務器沒有此資源,向服務器請求

服務器返回了資源,HTTP響應頭部信息Vary指定了 Accept-Language,代理服務器返回資源給客戶端,並緩存了數據。緩存如下

前端HTTP緩存簡單瞭解

第三個客戶端向服務器請求/sample.html資源,Accept-Language: zh-cn或者 en-us, 代理服務器都能返回緩存數據(緩存沒過期)。

所以:

服務器使用Vary字段來通知緩存哪些請求頭字段用於區分相同的URL請求,服務端存在不同內容的響應。

緩存也會根據Vary指定了 的字段X,根據X字段的值,決定使用緩存,還是發起請求獲取數據

上述例子是簡單描述存在代理服務器的請款,瀏覽器通常不實現針對每個URL存儲多個變體的功能。

感興趣可以查看:Understanding The Vary Header Caching Negotiated Responses

2.2 緩存流程

假設只有瀏覽器緩存和服務器的場景。參考以上字段畫圖如下

前端HTTP緩存簡單瞭解

2.2.1 流程分析

⑴ 當對資源發起請求的時候,緩存對url 報文進行解析,提取首部判斷客戶端是否有緩存。

沒有緩存,求直接向服務器端請求數據,當得到數據後按緩存控制存儲。

有緩存的情況:

  • 緩存需要驗證才可使用,向服務器發送本地緩存的相關的驗證字段(If-None-Match 、If-Modified-Since)到原始服務器進行驗證。
  • 緩存能用,是否過期
    • 緩存能用,沒有過期,構造響應報文,展示緩存內容
    • 緩存過期,發送本地緩存的相關的驗證字段(If-None-Match 、If-Modified-Since)到原始服務器進行驗證。

⑵ 向服務器發送本地緩存的相關的驗證字段(If-None-Match 、If-Modified-Since)到原始服務器進行驗證

條件方法再驗證(協商緩存):

  • 條件驗證成功:原始服務器向客戶端發送一個小的 HTTP 304 Not Modified 響應,不包括內容。客戶端緩存會更新緩存文檔的新鮮度,構造響展示緩存內容。
  • 驗證失敗:原始服務器向客戶端返回新的內容,客戶端緩存,展示新的內容。
  • 內容被刪除:原始服務器向客戶端發送一個 404 Not Found 響應,客戶緩存也會刪除緩存。

2.2.2 memory cache和 disk cache

當緩存足夠新鮮,直接返回緩存數據或者重新加載數據,數據 from memory cache 還是 from disk cache 不要太過糾結。這個跟HTTP 緩存機制沒關係。

Chrome employs two caches — an on-disk cache and a very fast in-memory cache. The lifetime of an in-memory cache is attached to the lifetime of a render process, which roughly corresponds to a tab. Requests that are answered from the in-memory cache are invisible to the web request API. If a request handler changes its behavior (for example, the behavior according to which requests are blocked), a simple page refresh might not respect this changed behavior. To make sure the behavior change goes through, call handlerBehaviorChanged() to flush the in-memory cache. But don’t do it often; flushing the cache is a very expensive operation. You don’t need to call handlerBehaviorChanged() after registering or unregistering an event listener.

大致意思就是:memory cache 的生存期與渲染過程的生存期相關,渲染過程的生存期大致與選項卡相對應。

Chrome優化可以詢問正在運行的進程,然後再在磁盤上查找它們是否仍在內存中加載了它們的副本。當頁面刷新或者加載,所有的內容文檔都會讀取到內存中展示,如果此時文檔在內存中已經存在,那麼緩存 from memory cache,如果是從磁盤中讀取的就 from disk cache 。

前端HTTP緩存簡單瞭解

我自己的簡單理解如上圖

如果感興趣資料:Disk Cache 3.0

3. 前端解決方案

前端HTTP緩存簡單瞭解

HTML:設置Cache-control:no-cache(服務器端配置)瀏覽器再每次請求時都始終重新驗證文檔,並在內容變化時獲取最新版本。
在HTML內掛載的 js css png 都帶上 文件唯一標識字符串。任意文件變化,url 就會變化,從而引起HTML 文件變化。下次請求資源就會更新。

4. 總結

文章整理了相關資料,記錄了部分實踐和自己的理解,理解不準確之處,還請教正。歡迎一起討論學習。

參考資料:

《圖解HTTP》

《HTTP權威指南》

Understanding The Vary Header

rfc2616

Disk Cache 3.0

http-caching

HTTP caching(MDN)

Caching Tutorial

What does Blink in-memory cache store?

相關文章

Docker學習筆記(三)——Docker常用命令

繼HTML、CSS和JavaScript之後,WebAssembly正式成為Web的第四種語言

從一道面試題簡單談談發佈訂閱和觀察者模式

是你需要的前端編碼風格嗎?