HTTP緩存——協商緩存(緩存驗證)

協商緩存

所謂「協商」,可以理解為:客戶端和服務端雙方商量着來

客戶端檢查資源超過有效期、強緩存命中失敗的情況下,則發出請求「詢問」服務器是否資源真的過期了,詢問的同時在請求頭要攜帶着資源的「上次更新時間」或者「唯一實體標識」(不同http版本導致的共存問題)。

服務端核對客戶端要請求的資源的「上次更新時間」或者「唯一實體標識」:

  • 若一致,說明命中協商緩存,只返回304;
  • 若不一致,說明資源有更新,則返回200、新資源,同時響應頭返回「資源修改時間」後者「資源最新的實體標識」。同時,客戶端拿到新的資源及其修改時間與標識後,重新進行緩存。

概括如下圖:

緩存驗證

協商緩存就是緩存驗證。

觸發時機:

  • 用戶點擊刷新按鈕時會開始緩存驗證。
  • 如果緩存的響應頭信息里含有”Cache-control: must-revalidate」的定義,在瀏覽的過程中也會觸發緩存驗證。
  • 另外,在瀏覽器偏好設置里設置Advanced->Cache為強制驗證緩存也能達到相同的效果。

觸發條件:

只有在服務器返回強校驗器或者弱校驗器時才會進行驗證。

附帶條件請求

形如 If-xxx 這種樣式的請求首部字段,都可稱為條件請求。
服務器接 收到附帶條件的請求後,只有判斷指定條件為真時,才會執行請求。
協商緩存中,就有很多這樣的附帶條件請求。

《圖解HTTP》

協商緩存 特點:

  • 服務端校驗
  • 304 Not Modified狀態碼
  • Last-Modified
  • Etag

304(Not Modified)

該狀態碼雖然是3XX的類別,但是跟301、302不一樣,不是重定向的含義。
304,Not Modified。表示服務端資源未改變,可直接使用客戶端緩存過的、未過期的資源。

他的觸發條件是:

1、客戶端採用GET方法,且在請求報文中含有「If-Match」、「If-Modified-Since」、「If-None-Match」、「If-Range」、「If-Unmodified-Since」等字段
2、服務器端接收到請求,允許請求並訪問資源。但因客戶端的請求未滿足條件,就直接返回了304。

304狀態碼返回時,不包含任何響應的主體部分。
也就是說,如果命中協商緩存,服務端響應請求時,只會返回一個304狀態碼、並沒有實際上的文件內容,因此在響應體體積上的節省是協商緩存的優化點

HTTP/1.0 Last-Modified組

Last-Modified (資源的最後修改日期時間)

實體首部字段:Last-Modified,表示資源最後被修改的時間

格式如:

last-modified: Thu, 01 Jan 1970 00:00:00 GMT

這句話就像是服務器告訴客戶端,你請求的這個文件是1970年1月1日修改的。

Last-Modified是一種緩存弱校驗器。說它弱是因為它只能精確到一秒。

如果響應頭裡含有這個信息,客戶端可以在後續的請求中帶上 If-Modified-Since 來驗證緩存:

If-Modified-Since (比較資源的更新時間)

請求首部字段

他是與Last-Modified對應的字段,存儲的是上次緩存的資源最終更新時間,也就是上次緩存資源時獲取的Last-Modified的值。

用於確認代理服務器/客戶端擁有的本地資源的有效性
如果在If-Modified-Since字段指定的日期時間後,資源發生了改變,服務器會接受請求。

上圖中,服務端拿着他的值和服務端本地被請求資源的Last-Modified進行比較:

  • 如果Last-Modified <= If-Modified-Since,說明資源一致,命中協商緩存,返回304狀態碼 Not Modified即可。
  • 如果Last-Modified > If-Modified-Since,說明資源被修改,需要返回最新資源給客戶端。

他的格式如:

if-modified-since: Thu, 01 Jan 1970 00:00:00 GMT

HTTP/1.1 Etag組

Etag (資源的匹配信息)

響應首部字段,緩存的一種強校驗器

實體標記(Etag)是與特定資源關聯的特定值,是資源唯一性標識的字符串。服務器會為每份資源分配對應的 ETag 值。 並通過響應頭首部字段告知客戶端資源的實體標識。

格式如:

etag: f7b80870fbcd8f9da18ab22d2ef1932c

特點:

  • 當資源更新時,ETag 值也需要更新。
  • 而且,生成 ETag 值時,並沒有統一的算法規則,而僅僅是由服務器來分配。所以分佈式服務器系統,一模一樣的一個文件的Etag值可能不一樣。
  • 此外,因為是按照內容不同來生成的唯一標識,中英文對應版本的資源,雖然地址相同,其Etag不同。

強弱Etag:

ETag 中有強 ETag 值和弱 ETag 值之分。

強ETag值

強 ETag 值,不論實體發生多麼細微的變化都會改變其值。

ETag: "usagi-1234"

弱ETag值

弱 ETag 值只用於提示資源是否相同。只有資源發生了根本改變,產 生差異時才會改變 ETag 值。

這時,會在字段值最開始處附加 「W/」。如下:

ETag: W/"usagi-1234" 

If-None-Match (比較實體標記)

請求首部字段

他是與Etag對應的字段,存儲的是上次緩存的資源的實體標記值,也就是上次緩存資源時獲取的Etag的值。

協商緩存時,客戶端攜帶該字段與服務端資源的Etag字段值進行比對,只有在If-None-Match的字段值與Etag值匹配不上、不一致時,命中協商緩存

GET或HEAD請求方法中,使用If-None-Match可獲取最新的資源。

格式如:

if-none-match: f7b80870fbcd8f9da18ab22d2ef1932c

他和If-Match的作用相反。

If-Match 與412 狀態碼

用法和規則基本同If-None-Match,但判斷邏輯完全相反。

If-Match的這個條件的判斷邏輯是:只有當 If-Match 的字段值跟 ETag 值匹配一致時才會命中協商緩存。服務器才會接受請求 並返回200和新數據。
反之,服務器返回狀態碼 412 Precondition Failed 的響應。

還可以使用 星號(*) 指定 If-Match 的字段值。

針對這種情況,服務器將會忽略 ETag 的值,只要資源存在就處理請求。

if-match: f7b80870fbcd8f9da18ab22d2ef1932c

或者

if-match: *

對比

首先,Etag的優先級高於Last-Modified。

Last-Modified和Etag的優缺點分析如下:

Last-Modified優點

不存在版本問題,每次請求都會去服務器進行校驗。服務器對比最後修改的時間,如果相同返回 304,不同的話返回 200 以及相應的數據資源

Last-Modified缺點

  1. 只要資源修改,無論內容是否發生實質性的變化,都會將該資源返回給客戶端。 例如周期性重寫,這種情況下該資源包含的數據實際上是一樣的;
  2. 以時刻作為標識,無法識別一秒內進行多次修改的情況。如果資源更新的速度是秒以下單位,那麼該緩存是不能被使用的,因為它的時間單位最低是秒;
  3. 某些服務器不能精確的得到文件最後修改時間; 如果文件是通過服務器動態生成的,那麼該方法的更新時間永遠是生成的時間,儘管文件可能沒有變化,所以起不到緩存的作用

Etag優點

  • 可以更加精確的判斷資源是否被修改,
  • 可以識別一秒內多次修改的情況;
  • 不存在版本問題,每次請求時都會去服務器進行校驗。

Etag缺點

  • 計算 Etag 值需要性能損耗;
  • 分佈式服務器時依賴算法:分佈式服務器存儲的情況下,計算 Etag 的算法如果不一樣,會導致瀏覽器從一台服務器上獲得頁面內容後到另外一台服務器上進行驗證時現 Etag 不匹配的情況。

兩組字段流程整理如下

最後再整體回顧、複習一下子。

(注意:實際HTTP1.1的請求中,兩組字段同時包含在請求及響應頭中,我這裡為了加深組CP的印象,分開闡述)

1、Last-Modified組整體流程如下:

  • 服務器通過 Last-Modified 字段告知客戶端,資源最後一次被修改的時間
  • 瀏覽器將這個值和內容一起記錄在緩存數據庫中
  • 下一次請求相同資源的時候,瀏覽器從自己的緩存中找出”不確定是否過期的”緩存。因此在請求頭中將上次的 Last-Modified 的值寫入到請求頭的 If-Modified-since 字段
  • 服務器會將 If-Modified-since 的值與 If-Modified 字段進行對比。如果相等,則表示未修改,響應 304;反之,表示修改響應 200 狀態碼並返回數據

2、Etag組整體流程如下:

瀏覽器在發起請求時,服務器在響應頭中返回請求資源的唯一標識。在下一次請求時,會將上一次返回的 Etag 值賦值給 If-None-match 並添加在響應頭中。服務器將瀏覽器傳來的 if-no-matched 跟自己的本地的資源的 Etag 做對比,如果匹配,則返回 304 通知瀏覽器讀取本地緩存,否則返回 200 和更新後的資源。