天天看點

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

轉載:http://mp.weixin.qq.com/s/uWPls0qrqJKHkHfNLmaenQ

Http 緩存機制作為 web 性能優化的重要手段,對從事 Web 開發的小夥伴們來說是必須要掌握的知識,但最近我遇到了幾個緩存頭設定相關的題目,發現有好幾道題答錯了,有的甚至在知道了正确答案後依然不明白其原因, 可謂相當的郁悶呢!!為了确認下是否隻是自己了解不深,我特意請教了其他幾位小夥伴,發現情況也或多或少和我類似。

為了不給大家賣關子,下面我貼出2道題,大家可以嘗試解答下:

以下為 page.html 内容:

首次通路該頁面,頁面中 head.png 響應頭資訊如下:

問題1:請問當點選“重新通路 page 頁”連結重新加載該頁面後, head.png 如何二次加載?

問題2:如果将上述資訊中的 Cache-Control 設定為 private,那麼結果又會如何呢?

以上2道題,如果你能全部答對(哈哈,還請仔細确認下 why,以防歪打正着),那麼恭喜你,你已對這些知識了解非常透徹了,我後面講的内容你可以忽略,否則還請繼續陪我往下唠唠吧!

首 先回到開篇提到很多小夥伴(包括我)在解答 Http 緩存題目時栽跟頭的問題,我覺得出現這種現象的根本原因在于我們吸收的知識還不夠體系化,平時我們在學習這些知識時多半将其當作知識點來記,什麼這個緩存 頭作什麼、那個緩存頭作什麼用的,但實際中緩存頭往往是多個之間互相配合協同工作的,有一套完整的工作體系。

今天我将按自己的了解,從系統體系化角度來講講 Http 緩存頭是如何協同工作的(不正确的地方還請指正,但請不要噴我哦):

首先我将 Http 緩存體系分為以下三個部分:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

用來确定 Http 響應内容是否可以被用戶端緩存,以及可以被哪些用戶端緩存

這個政策的作用隻有一個,用于決定 Http 響應内容是否可緩存到用戶端

對 于 Cache-Control 頭裡的 Public、Private、no-cache、max-age 、no-store 他們都是用來指明響應内容是否可以被用戶端存儲的,其中前4個都會緩存檔案資料(關于 no-cache 應了解為“不建議使用本地緩存”,其仍然會緩存資料到本地),後者 no-store 則不會在用戶端緩存任何響應資料。另關于 no-cache 和 max-age 有點特别,我認為它是一種混合體,下面我會講到。

通 過 Cache-Control:Public 設定我們可以将 Http 響應資料存儲到本地,但此時并不意味着後續浏覽器會直接從緩存中讀取資料并使用,為啥?因為它無法确定本地緩存的資料是否可用(可能已經失效),還必須借 助一套鑒别機制來确認才行, 這就是我們下面要講到的“緩存過期政策”。

用戶端用來确認存儲在本地的緩存資料是否已過期,進而決定是否要發請求到服務端擷取資料

這個政策的作用也隻有一個,那就是決定用戶端是否可直接從本地緩存資料中加載資料并展示(否則就發請求到服務端擷取)

剛 上面我們已經闡述了資料緩存到了本地後還需要經過判斷才能使用,那麼浏覽器通過什麼條件來判斷呢? 答案是:Expires,Expires 指名了緩存資料有效的絕對時間,告訴用戶端到了這個時間點(比照用戶端時間點)後本地緩存就廢棄了,在這個時間點内用戶端可以認為緩存資料有效,可直接從 緩存中加載展示。

不過 Http 緩存頭設計并沒有想象的那麼規矩,像上面提到的 Cache-Control(這個頭是在Http1.1裡加進來的)頭裡的 no-cache 和 max-age 就是特例,它們既包含緩存存儲政策也包含緩存過期政策,以 max-age 為例,他實際上相當于:

而 Cache-Control:no-cache 和 Cache-Control:max-age=0 (機關是秒)相當

這裡需要注意的是:

Cache-Control 中指定的緩存過期政策優先級高于 Expires,當它們同時存在的時候,後者會被覆寫掉。

緩存資料标記為已過期隻是告訴用戶端不能再直接從本地讀取緩存了,需要再發一次請求到伺服器去确認,并不等同于本地緩存資料從此就沒用了,有些情況下即使過期了還是會被再次用到,具體下面會講到。

将緩存在用戶端的資料辨別發往服務端,服務端通過辨別來判斷用戶端 緩存資料是否仍有效,進而決定是否要重發資料。

客 戶端檢測到資料過期或浏覽器重新整理後,往往會重新發起一個 http 請求到伺服器,伺服器此時并不急于傳回資料,而是看請求頭有沒有帶辨別( If-Modified-Since、If-None-Match)過來,如果判斷辨別仍然有效,則傳回304告訴用戶端取本地緩存資料來用即可(這裡要 注意的是你必須要在首次響應時輸出相應的頭資訊(Last-Modified、ETags)到用戶端)。至此我們就明白了上面所說的本地緩存資料即使被認 為過期,并不等于資料從此就沒用了的道理了。

關于 Last-Modified,這個響應頭使用要注意,可能會影響到緩存過期政策,具體原因,後面我會通過解答開篇提到的2道題來作說明。

以上就是我所認識的緩存政策,下面我将緩存政策三要素和常用的幾個緩存頭(項)結合一起,讓大家更清晰的認識到它們之間的關系:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

通過上圖我可以清晰的看到各緩存項分别屬于哪個緩存政策範疇,這其中有部分重疊,它表明這些緩存項具有多重緩存政策,是以實際在分析緩存頭的時候,除了正常的頭外,我們還需要将這些具有雙重緩存政策的項分解開來。

最後我們回到最開始提到的2道題目,我們來一起分解下:

第一道題:

分析上述 Http 響應頭發現有以下兩項與緩存相關:

我們上面講到了 Cache-Control: no-cache 相當于 Cache-Control: max-age=0,且他們都是多重政策頭,我們需将其分解:

Cache-Control: no-cache 等于 Cache-Control: max-age=0,

接着 Cache-Control: max-age=0 又可分解成:

最終我們得到了以下完整的緩存政策三要素:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

是以最終結果是:浏覽器會再次請求服務端,并攜帶上 Last-Modified 指定的時間去伺服器對比:

a)對比失敗:伺服器傳回200并重發資料,用戶端接收到資料後展示,并重新整理本地緩存。

b)對比成功:伺服器傳回304且不重發資料,用戶端收到304狀态碼後從本地讀取緩存資料。以下為模拟此種情況下請求後的抓包情況:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

這道題本身不難,但若認為 no-cache 不會緩存資料到本地,那麼你了解起來就會很沖突,因為如果檔案資料沒有被本地緩存,伺服器傳回304後将會無法展示出圖檔内容,但實際上它是能正常展示的。這道題很好的證明了 no-cache 也會緩存資料到本地這一說法。

第二道題:

解題思路和上題一樣,首先先找到緩存相關項:

這時我們會發現根本找不到緩存過期政策項,那答案會不會和上面一樣? 一時半會也分析不出答案,那隻能實際測試下了:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

再看看 Chrome 浏覽器下抓包:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

可以看到,浏覽器後續請求都直接取的本地緩存,看來的确存在某種緩存過期政策(根據我上面的緩存過期政策理論,浏覽器如果直接從本地加載緩存資料,說明它 相信本地緩存資料有效,那一定存在某種緩存過期判斷條件)。這個問題百思不得其解,困擾了我好久,直到一次偶然的機會我在 Fiddler 響應資訊面闆裡的 Caching 頁籤中找到了答案:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

原來,在沒有提供任何浏覽器緩存過期政策的情況下,浏覽器遵循一個啟發式緩存過期政策:

根據響應頭中2個時間字段 Date 和 Last-Modified 之間的時間內插補點,取其值的10%作為緩存時間周期。

貼一下Caching面闆裡的描述,英語好的同學可以精準翻譯下:

Http協定:徹底弄懂 Http 緩存機制 - 基于緩存政策三要素分解法

浏 覽器會根據 Date 和 Last-Modified 之間的時間內插補點緩存一段時間,這段時間内會直接使用本地緩存資料而不會再去請求伺服器(強制請求除外),緩存過期後,會再次請求服務端,并攜帶上 Last-Modified 指定的時間去伺服器對比并根據服務端的響應狀态決定是否要從本地加載緩存資料。

Http 緩存設定起來并不複雜,但卻容易被輕視, 今天這篇文章結合2道題目,通過分析、解剖相關緩存頭,從系統化角度對 Http 緩存機制做了一個較完整的剖析:Http 緩存機制實際上是 Http 緩存政策三個要素(緯度)互相作用的集合,是以在分析和設定 Http 封包緩存頭時,隻要能從中精準的分解出緩存三要素,我們就能非常準确的預判到緩存設定最終能達到的效果。