輕鬆理解 HTTP 緩存策略

2021-02-25 博客園

上一篇文章我寫了koa-static的源碼解析[1],其中用到了HTTP的緩存策略,給返回的靜態文件設置了一些緩存的頭,比如Cache-Control之類的。於是我就跟朋友討論了一下HTTP的緩存策略:

朋友說:「HTTP裡面控制緩存的頭(header)太多了,啥Cache-Control,ETag,Last-Modified,一大堆,亂七八糟的,而且之間邏輯關係不強,要掌握基本靠背!」

我有點驚訝:「為什麼要去背這個呢?所有的技術都是為了解決問題而存在的,不了解問題而去單純的學習技術,去,背,去,死記,確實很枯燥,而且效果不好。

HTTP緩存策略只是為了解決客戶端和服務端信息不對稱的問題而存在的,客戶端為了加快速度會緩存部分資源,但是下次請求時,客戶端不知道這個資源有沒有更新,服務端也不知道客戶端緩存的是哪個版本,不知道該不該再返回資源,其實就是一個信息同步問題,HTTP緩存策略就是來解決這個問題的

如果我們跳出這種純粹的技術思維,我們會發現生活中這種信息同步問題也很常見。而我們解決這些問題的思路很多時候都是司空見慣了,如果從這個角度來說,這個問題就很好理解!」

於是我給他講了一個我小時候租光碟看奧特曼的故事。

租光碟看奧特曼

事情是這樣的,我小時候特別喜歡看動畫片,尤其是奧特曼,但是那時候沒有電腦啊,也沒有網絡。我只有一臺DVD播放機,於是我會經常跑去租光碟的店租奧特曼。

ETag某天,我看完了《艾斯奧特曼》第10集,我還想繼續看。於是我找到了光碟店的老闆:「老闆,第10集我看完了哦,你還有沒有新的啊?」老闆說:「有有有,剛出了第11集,你拿去吧!」上面這一個簡單的交流過程其實就包含了一個HTTP的緩存技術,那就是ETag!類比於網絡請求,我其實就是客戶端,光碟店就是服務端,我去租光碟就相當於發起一個請求。但是我去租光碟時,老闆並不知道我看到哪集了,我們的信息是不同步的。

所以我告訴了他一個標記(Tag),在這裡這個標記就是第10集,老闆拿到這個標記,跟他自己庫存的標記比較一下,發現他最新標記是第11集,於是知道有更新了,將第11集給了我。

Last-Modified & If-Modified-Since

再來,我《艾斯奧特曼》看完了,我開始看《泰羅奧特曼》了。可是老闆這次比較雞賊,《泰羅奧特曼》沒買正版的,是他自己翻錄的,他翻錄的時候自己也不知道是第幾集,但是他聰明的在光碟上寫上了翻錄日期。

於是我正在看的這盤也沒啥封面,只光禿禿的寫了一個2000年12月1日。當我這盤看完了,我又去找老闆了:「老闆,你這個2000年12月1日的我已經看完了,你還有沒有新的啊?」

這裡的2000年12月1日其實就是標記了我手上副本的更新日期,這也對應了HTTP的一個緩存技術,那就是Last-ModifiedIf-Modified-Since

你可以理解為,老闆給日期還取了一個名字,叫Last-Modified,所以光碟上完整文字是Last-Modified:2000年12月1日,而我去問的時候就這麼問:「Do you have any updates IF-Modified-Since 2000年12月1日?」。

Expires 和 Max-Age

繼續,我《泰羅奧特曼》也看完了,開始看《雷歐奧特曼》了。這《雷歐奧特曼》跟前面兩個都不一樣,我去租的時候老闆就說了:「你小子別天天跑來問了!《雷歐奧特曼》我每周去進一次貨,你每周一來拿就行!」這句話也對應了一個HTTP緩存技術,那就是ExpiresMax-Age

我知道了下周一之前,我手上都是最新的,到了下周一就過期(Expire)了。所以「我手上的是最新的」這個說法有個生命周期,他的年齡是有限的,他的年齡等於下周一更新時間減去當前時間,這就是他的最大年齡(Max-Age)。Immutable再來一個,我《雷歐奧特曼》也看完了,開始看《奈克斯特奧特曼》了。這《奈克斯特奧特曼》跟前面幾個都不一樣,我去租的時候老闆說了:「小子,你這次運氣好,這《奈克斯特奧特曼》已經出完了,你全部拿去吧,也不用天天跑來問了!」

這句話對應的HTTP緩存技術是啥?當然是Immutable!Immutable就跟字面意思一樣,不可變的!就像《奈克斯特奧特曼》一樣,已經出完了,不用再去問更新了。

言歸正傳扯蛋到這裡結束,咱們言歸正傳!之所以舉這麼個例子,是為了說明HTTP緩存技術要解決的問題在生活中很常見,從這些常見的場景入手,理解起來更簡單。

下面我們正兒八經的來說說HTTP緩存技術:

兩種機制從上面的幾個小例子可以看出,有時候為了知道是不是有更新,我必須去問老闆,比如第一個例子裡面:「老闆,第10集我看完了哦,你還有沒有新的啊?」。這種為了知道有沒有更新,必須跟服務端溝通過才知道的,我們稱之為協商緩存。還有些場景,我不去問就知道有沒有更新,比如第三個例子,因為知道是周更的,當周一來之前,我都不會去問了,到了周一再去問,這種不用跟伺服器協商直接用本地副本的叫做強制緩存

換成技術的話說就是,強制緩存不用發請求直接用本地緩存,協商緩存要發請求去問伺服器有沒有更新。下面我們詳細來講下這兩種緩存:

協商緩存

前面第一個例子和第二個例子每次都需要向伺服器端詢問,所以是協商緩存

ETag 和 If-None-MatchETag是URL的Entity Tag,就是一個URL資源的標識符,類似於文件的md5,計算方式也類似,當伺服器返回時,可以根據返回內容計算一個hash值或者就是一個數字版本號,類似於我們的第10集,具體返回什麼值要看伺服器的計算策略。然後將它加到responseheader裡面,可能長這樣:

ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

客戶端拿到後會將這個ETag和返回值一起存下來,等下次請求時,使用配套的If-None-Match,將這個放到requestheader裡面,可能長這樣:

If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

然後服務端拿到請求裡面的If-None-Match跟當前版本的ETag比較下:

1.如果是一樣的話,直接返回304,語義為Not Modified,不返回內容(body),只返回header,告訴瀏覽器直接用緩存。

2.如果不一樣的話,返回200和最新的內容

與ETag配套的還有一個不太常用的request header----If-Match,這個和前面If-None-Match的語義是相反的。前面If-None-Match的語義是如果不匹配就下載。而If-Match通常用於post或者put請求中,語義為如果匹配才提交,比如你在編輯一個商品,其他人也可能同時在編輯。當你提交編輯時,其他人可能已經先於你提交了,這時候服務端的ETag就已經變了,If-Match就不成立了,這時候服務端會給你返回412錯誤,也就是Precondition Failed,前提條件失敗。如果If-Match成立,就正常返回200。Last-Modified & If-Modified-Since

Last-Modified和If-Modified-Since也是配套使用的,類似於ETag和If-None-Match的關係。只不過ETag放的是一個版本號或者hash值,Last-Modified放的是資源的最後修改時間。

Last-Modified是放到response的header裡面的,可能長這樣:

Last-Modified: Wed, 21 Oct 2000 07:28:00 GMT

而客戶端瀏覽器在使用時,應該將配套的If-Modified-Since放到requestheader裡面,長這樣:
If-Modified-Since: Wed, 21 Oct 2000 07:28:00 GMT

服務端拿到這個頭後,會跟當前版本的修改時間進行比較:

1.當前版本的修改時間比這個晚,也就是這個時間後又改過了,返回200和新的內容

2.當前版本的修改時間和這個一樣,也就是沒有更新,返回304,不返回內容,只返回頭,客戶端直接使用緩存

與If-Modified-Since對應的還有If-Unmodified-Since,If-Modified-Since可以理解為有更新才下載,那If-Unmodified-Since就是沒有更新才下載

如果客戶端傳了If-Unmodified-Since,像這樣:

If-Unmodified-Since: Wed, 21 Oct 2000 07:28:00 GMT

服務端拿到這個頭後,也會跟當前版本的修改時間進行比較:

1.如果這個時間後沒有更新,伺服器返回200,並返回內容。

2.如果這個時間後有更新,其實就是這個if不成立,會返回錯誤代碼412,語義為Precondition FailedETag 和 Last-Modified優先級ETag和Last-Modified都是協商緩存,都需要伺服器進行計算和比較,那如果這兩個都存在,用哪個呢?答案是ETag,ETag的優先級比Last-Modified。因為Last-Modified在設計上有個問題,那就是Last-Modified的精度只能到秒,如果一個資源頻繁修改,在同一秒進行多次修改,你從Last-Modified上是看不出來區別的。但是ETag每次修改都會生成新的,所以他比Last-Modified精度高,更準確。但是ETag也不是完全沒問題的,你的ETag如果設計為一個hash值,每次請求都要計算這個值,需要額外耗費伺服器資源。具體使用哪一個,需要根據自己的項目情況來進行取捨。強制緩存上面扯蛋那裡的第三個例子和第四個例子就是強制緩存,就是我知道在某個時間段完全不用去問服務端,直接去用緩存就行。這兩個例子裡面提到的Expires是一個單獨的header,max-age和immutable同屬於Cache-Control這個header。Expires

Expires比較簡單,就是伺服器response的header帶上這個欄位:

Expires: Wed, 21 Oct 2000 07:28:00 GMT

然後在這個時間前,客戶端瀏覽器都不會再發起請求,而是直接用緩存資源。

Cache-Control

Cache-Control相對比較複雜,可設置屬性也比較多,max-age只是其中一個屬性,長這樣:

Cache-Control: max-age=20000

這表示當前資源在20000秒內都不用再請求了,直接使用緩存。上面提到的immutable也是Cache-Control的一個屬性,但是是個實驗性質的,各個瀏覽器兼容並不好。設置了Cache-control: immutable表示這輩子都用緩存了,再請求是不可能的了。no-cache:使用緩存前,強制要求把請求提交給伺服器進行驗證(協商緩存驗證)。no-store:不存儲有關客戶端請求或伺服器響應的任何內容,即不使用任何緩存。另外Cache-Control還有很多屬性,大家可以參考MDN的文檔[2]。Expires 和 Cache-Control 的優先級就一句話:如果在Cache-Control響應頭設置了 max-age 或者 s-maxage 指令,那麼 Expires 頭會被忽略。協商緩存和強制緩存優先級這個其實很好理解,協商緩存需要發請求跟伺服器協商,強制緩存如果生效,根本就不會發請求。所以這個優先級就是:先判斷強制緩存,如果強制緩存生效,直接使用緩存;如果強制緩存失效,再發請求跟伺服器協商,看要不要使用緩存。總結

本文從生活中常見的場景入手,闡述了HTTP緩存機制其實是提高訪問速度和解決信息不同步的一種機制。這種信息不同步在生活中很常見,很多解決思路我們已經司空見慣,帶著這種思維,我們可以很好的理解HTTP緩存機制。

HTTP緩存機制要點如下:

1.HTTP緩存機制分為強制緩存協商緩存兩類。

2.強制緩存的意思就是不要問了(不發起請求),直接用緩存吧。

3.強制緩存常見技術有Expires和Cache-Control。

4.Expires的值是一個時間,表示這個時間前緩存都有效,都不需要發起請求。

5.Cache-Control有很多屬性值,常用屬性max-age設置了緩存有效的時間長度,單位為秒,這個時間沒到,都不用發起請求。

6.immutable也是Cache-Control的一個屬性,表示這個資源這輩子都不用再請求了,但是他兼容性不好,Cache-Control其他屬性可以參考MDN的文檔[3]。

7.Cache-Control的max-age優先級比Expires高。

8.協商緩存常見技術有ETag和Last-Modified。

9.ETag其實就是給資源算一個hash值或者版本號,對應的常用request header為If-None-Match。

10.Last-Modified其實就是加上資源修改的時間,對應的常用request header為If-Modified-Since,精度為秒。

11.ETag每次修改都會改變,而Last-Modified的精度只到秒,所以ETag更準確,優先級更高,但是需要計算,所以服務端開銷更大。

12.強制緩存協商緩存都存在的情況下,先判斷強制緩存是否生效,如果生效,不用發起請求,直接用緩存。如果強制緩存不生效再發起請求判斷協商緩存。參考資料:ETag MDN文檔:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/ETagLast-Modified MDN文檔:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Last-ModifiedExpires MDN文檔:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/ExpiresCache-Control MDN文檔:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control[1] koa-static的源碼解析: https://juejin.cn/post/6903350655474204680

作者:_蔣鵬飛

博客園文章地址: 

cnblogs.com/dennisj/p/14167821.html

相關焦點

  • 深入理解 HttpSecurity【源碼篇】
    因此我們有必要從源碼的角度來理解下 HttpSecurity 到底幹了啥?這一點,小夥伴們可以回憶前面【深入理解 FilterChainProxy【源碼篇】】一文。首先 AbstractConfiguredSecurityBuilder 中定義了一個枚舉類,將整個構建過程分為 5 種狀態,也可以理解為構建過程生命周期的五個階段,如下:private enum BuildState { UNBUILT(0), INITIALIZING
  • 營銷講堂 | SWOT營銷策略分析方法!
    閒置信息輕鬆賺賞金金刺蝟招商APP為你打開財富大門(識別上圖二維碼即可下載當然,應用SWOT分析可以幫助企業制定行之有效的4Ps營銷策略,這可以通過以下三步分析得出。首先,研究者列舉企業某項業務內因的優勢和劣勢,以及競爭對手的主要優勢和劣勢。競爭對手的某些優勢往往是目標企業的劣勢。優勢劣勢包括產品質量、技術能力、人力資源、品牌和銷售能力等企業內部因素的所有方面。
  • 從平庸走向卓越的最佳策略是什麼
    哪有什麼策略能夠讓自己變得更加的出眾呢?或者說在某個技能上(或者說某個維度上)死磕,確實是一個策略,但也不一定是不好的策略。借用幾何術語的話,其實很容易理解:1.單個維度上,大家比的是長度;2.三個維度上,大家比的是體積……(有興趣的朋友可以在邏輯思維上購買《超級個體》音頻或者在網上購買這本書,教會我們新的觀念和思考方式)而實際上生活有很多個維度,每個人也都是立體的,不是平面的,更不是一根線——生活中有很多特別精準卻沒有被大多數人可以去理解的比喻,比如,「一根筋」,就是個細思懼恐地準確比喻……在任何一個單一的維度上
  • 為什麼說誠實是一種策略?
    一個誠實的人我們相處起來會比較輕鬆容易,但是一個人光靠品德修行的誠實是不夠的,還需要有其他約束懲罰措施。比如我們在一件小事面前可能是誠實的,在一件大事可能不誠實,尤其是涉及很大經濟利益情況下,如果沒有相關懲罰措施就可能變質。
  • 傳說中的德撲最優策略--GTO,到底是什麼鬼
    所以「GameTheory Optimal」很自然地會被翻譯成:博弈論最優,至少這是百度翻譯給出的,此外還有「最優遊戲策略」等等類似的翻譯。問題是,到目前為止,所有這些翻譯給我們普通人帶來的正常理解,跟GTO理論本身的含義,是不同的,這是GTO給大多數人帶來困惑的首要原因。這樣,我們只能回頭來,先不用管它中文怎麼稱呼,看看GTO到底是什麼意思。
  • 餐廳營銷策略大全,好生意離不開這幾點
    可是現在的餐廳實在是太多了,創業者想要突出自己餐廳的話,必須要使用一些營銷策略才行,接下來一起來了解下。  一、餐廳的價格營銷策略餐廳在定價的時候,一定要打破客戶心理價格防線,這才是一個正確的價格營銷策略。
  • 8個記憶策略讓你過目不忘 精選推薦
    下面為大家介紹最有效的8個記憶策略——如果你能在明天記住全部8個策略的話會加分哦。一個匿名用戶引用了博士Bill Klemm的一篇文章,該文章強調略讀是保持信息的一個關鍵策略。Bill Klemm博士是神經科學專家。這個略讀策略不等於跳過這個閱讀過程,而是在閱讀之前你會想要跳過文本內容去找一些重要的論題和關鍵詞,這樣在真正看文章的時候你就會對特定內容有所期待。Klemm博士認為,熟悉文章主題能幫助你記住文章的細節部分。
  • 營銷講堂丨SWOT營銷策略分析方法!
    當然,應用SWOT分析可以幫助企業制定行之有效的4Ps營銷策略,這可以通過以下三步分析得出。首先,研究者列舉企業某項業務內因的優勢和劣勢,以及競爭對手的主要優勢和劣勢。競爭對手的某些優勢往往是目標企業的劣勢。優勢劣勢包括產品質量、技術能力、人力資源、品牌和銷售能力等企業內部因素的所有方面。
  • C語言學習指針的秘笈:輕鬆理解(*(void (*)())t)()
    不然,下面和小編一起,輕鬆搞定它!先明確「void (*)()」的含義:它表示一個數據類型,這個數據類型是個函數的指針,所指向的函數無參數無返回值。你怎麼知道?如果用「指針的定義形式逆序閱讀法」解決,就是小菜一碟!
  • 如何理解「自律者自由」?
    在近 30 年的人生歷程中,我對這句話的理解發生了非常顯著的變化,今天我就來聊聊在不同階段,我對「自律」的理解變遷。1. 自律不是一件道德高尚的事首先,明確一點:自律只是一種能力,而非品格。我曾經把那些自由不羈的同齡人歸因 為懶惰和放縱,覺得他們缺乏自律,甚至將他們排出在好人之列,覺得她們看起來的自由自在,實際上只是墜入無盡黑暗前的末日狂歡。
  • Excel表格插入公司LOGO技巧,頁眉設置輕鬆簡單,從此不加班
    進入頁眉圖片的格式設置,大家注意標尺線,理解頁邊距和頁眉寬度的關係,調整圖片的大小,以適應表格的位置。預覽中第一頁的LOGO不見了,後面每頁都有LOGO,輕鬆簡單。最近公眾號以及模板網站http://www.2xx.vip/將有一系列改版,可能細心的小夥伴已經感覺出了變化,目的是多方位呈現以及多樣性服務,畢竟公眾號排版查詢限制太多,歡迎大家注意模板網站,支持幫幫,麼麼噠。
  • 混合策略
    為了能夠成為一個好的撲克選手,他花費了相當多的時間去思考如何玩撲克以及分析其他選手的策略。      經過不懈的努力,漸漸的他擁有一個非同尋常的才能——「讀懂」對手。他發現有兩種方式可以獲得對手的信息:一種是對手有意識地或者下意識地動作會透漏出有一手好牌;另一種是對手思考一手牌以及出牌的方式。克裡斯·弗格森通過這兩種方式正確地解讀對手下意識的信號,並針對對手調整玩法。
  • 時間管理策略(下)
    嗨,我是靈犀接著前面的第一期 和 第二期 策略五 串聯和並聯