摘要:緩存主要通過首部來控制。表示當前響應數據是單個用戶所獨占的,只能被客戶端緩存,不能被代理服務器緩存。其值是任意整數,和負數表示緩存過期,正數值加上當前響應頭中的首部值即為過期時間。客戶端收到后,直接使用緩存的,同時更新緩存有效期。
無論是軟件應用還是硬件應用,緩存都扮演著重要的角色,其對提升性能的重要性無可置疑。
本文主要介紹 HTTP 緩存,涉及其原理和應用。HTTP 緩存主要通過 HTTP 首部來控制。
緩存示例先看一個簡單的緩存示例:
瀏覽器首次請求 app.js 時,服務器會返回資源內容和相關頭部,其中 Cache-Control: max-age=120 告訴瀏覽器說,這個資源的緩存有效期為 120 秒,從當前時間 Date: Mon, 05 Mar 2018 08:00:00 GMT 開始算起。瀏覽器收到資源后便將 app.js 及其相應頭部存儲在本地。
如果在 05 Mar, 2018 08:02:00 GMT 之前再次請求 app.js ,則瀏覽器會直接使用存儲在本地的資源,而不用再次向服務器發起請求。
這個過程中,我們就說 app.js 被緩存且命中了。
基本概念在進一步理解緩存之前,先看下跟緩存相關的幾個概念:
命中:請求數據不需再次下載,可以直接使用緩存數據
過期:緩存數據超過設置的有效時間,將被標記為“陳舊”
驗證:判斷過期緩存是否仍然有效,需要與服務器交互
失效:緩存數據不再有效,需要從服務端重新下載新數據
基本理解HTTP 緩存涉及到請求-響應鏈上的多個角色,包括客戶端(本文指瀏覽器)、代理和服務器。
其中,瀏覽器自身也實現了緩存功能。瀏覽器在請求資源時,總是先從本地緩存中查找,如果找到未過期資源,則直接使用,否則向服務器發起請求。
代理也是服務器的一種,但一般情況下不會把它多帶帶抽出來分析,只有在跟它有關的地方會把它區分于源服務器。所以,下文的示例圖中將不會把它列進去。
HTTP 緩存的理解基本上可以總結為三個問題:
緩存數據可以存儲在哪些設備上?(WHERE)
緩存數據如何判斷過期?(HOW)
過期緩存內容是否真的需要重新下載?(WHETHER)
問題 1 說明存儲緩存數據的設備是多樣的,可以存儲于各級代理服務器,也可以存儲于瀏覽器本地。
問題 2 說明使用什么辦法來判斷緩存數據是否已經過期,當然是比較時間啦,那么如何比較呢?
問題 3 說明緩存雖然過期了,但是其內容仍然可能與服務端一致,這時就沒必要重新下載相同數據,只需要向服務端詢問下是否可以繼續使用緩存即可。
帶著上面三個問題去理解 HTTP 緩存頭部設置會更有助于理解和記憶。
有人根據是否需要進行問題 3 中的重新驗證把緩存策略的設置分為強緩存和協商緩存,強緩存無須再次驗證的緩存策略,協商緩存是需要再次驗證的緩存策略。
兩者的區別在于,協商緩存多發起了一次 HTTP 請求。
HTTP 緩存主要通過 HTTP 首部來實現緩存控制。這些與緩存相關的 HTTP 首部這里統稱為緩存首部,具體首部如下表所示。
首部字段 | 首次定義 | 首部類型 |
---|---|---|
Pragma | HTTP/1.0 | 通用首部 |
Age | HTTP/1.1 | 響應首部 |
Expires | HTTP/1.0 | 實體首部 |
Cache-Control | HTTP/1.1 | 通用首部 |
Etag | HTTP/1.1 | 響應首部 |
If-Match | HTTP/1.1 | 請求首部 |
If-None-Match | HTTP/1.1 | 請求首部 |
If-Modified-Since | HTTP/1.0 | 請求首部 |
Last-Modified | HTTP/1.0 | 實體首部 |
其中,“首次定義”是指首次出現在哪個 HTTP 版本。之所以列出這項內容,是因為實際應用需要考慮兼容舊版 HTTP 。
現代的 HTTP 緩存策略主要使用 Cache-Control 實現,它是目前最新的緩存首部,用于取代較老的緩存首部如 Pragma 、Expires 等。所以應用中應該傾向于使用 Cache-Control 。但是為了支持只實現了 HTTP/1.0 的客戶端設備,服務端通常還是都會同時設置 Expires、Pragma 和 Cache-Control 等,此時 Cache-Control 會有更高的優先級。提醒一下,現代瀏覽器都已支持 Cache-Control 。
Cache-ControlCache-Control 是通用首部,這意味著它既可以出現在請求中,也可以出現在響應中。
Cache-Control 的值可由多個字段組合而成,以逗號分隔,如 Cache-Control: private,max-age=3600 。下面對常用的可取字段進行說明。
public: 表示當前響應數據所有用戶共享的,可以被任何設備緩存,包括客戶端、代理服務器等。
private: 表示當前響應數據是單個用戶所獨占的,只能被客戶端緩存,不能被代理服務器緩存。
max-age=
max-stale[=
min-fresh=
s-maxage=
no-cache: 并非字面意思,它并非禁止緩存,而是強制在使用已緩存數據之前,需要去服務端驗證一下是否可以使用緩存數據。
no-store: 真正的禁止緩存,任何設備都不允許緩存,每次請求都需要向服務端重新獲取數據。
no-transform: 表示響應的實體數據不應被轉換。Content-Encoding 、Content-Range 和 Content-Type 首部也不能被修改。實際應用中,有些代理服務器會對圖片資源進行格式轉換以節省空間或者帶寬。
作為通用首部,其部分指令值可以出現在請求首部,也可以出現在響應首部,兩者可能略有區別:
指令值 | 請求 | 響應 |
---|---|---|
public | - | 可共享數據,可被任何設備緩存 |
private | - | 用戶私有數據,只能被客戶端緩存 |
no-cache | 使用前需驗證 | 使用前需驗證 |
no-store | 禁止使用緩存數據 | 禁止緩存 |
max-age | 要求資源的 age 小于這個時間 | 最大過期時間 |
min-fresh | 要求資源至少還剩余多少過期時間 | - |
max-stale | 超過過期時間多少秒內仍愿意接受 | - |
no-transform | 不要轉換格式 | 不要轉換格式 |
這些指令用在請求首部的情況比較少見,最可能接觸的地方是 Chrome DevTools 中的 Network 標簽頁。
其中,有個 Disable cache 選項,選中后 DevTools 會自動給所有請求頭部加上 Cache-Control: no-cache 首部,以告訴瀏覽器和代理使用本地緩存之前必須先驗證。
If-Modified-Since 首部比較的是資源的修改時間,精度為秒,是一種緩存過期后的常用驗證方式。一般來說,驗證資源是否修改過,對比資源的修改時間是一種最簡單的辦法。
使用過程如下:
客戶端首次請求 app.js 時,服務器響應帶上 Last-Modified 首部,告訴客戶端當前資源的最后修改時間。客戶端根據 Cache-Control: max-age=120 ,把 app.js 和響應首部緩存起來。
客戶端再次發起請求 app.js 時,把之前保存的 Last-Modified 時間放入 If-Modified-Since 首部發給服務器。服務器發現資源的 Last-Modified 時間沒有發生改變,于是直接響應 304 。客戶端收到 304 后,直接使用緩存的 app.js ,同時更新緩存有效期。
客戶端再次發起請求 app.js 時,把之前保存的 Last-Modified 時間放入 If-Modified-Since 首部發給服務器。服務器發現資源的 Last-Modified 時間已經發生改變,于是響應 200 ,將修改后的 app.js 和新的 Last-Modified 發送給客戶端。客戶端收到 200 后,重新下載新的 app.js ,并把新的 app.js 和響應首部緩存起來,替換原先的舊緩存。
ETag/If-Matched/If-None-MatchETag 叫實體標簽(Entity Tag),用于表示實體資源是否發生變化,其生成原理類似 MD5 ,也是一種用于驗證的首部。當響應的首部信息或者消息實體發生變化時,實體標簽也會改變。
使用過程如下:
客戶端首次請求 app.js 時,服務器響應帶上 ETag 首部,告訴客戶端當前資源的實體標簽。客戶端根據 Cache-Control: max-age=120 ,把 app.js 和響應首部緩存起來。
客戶端再次發起請求 app.js 時,把之前保存的 ETag 值放入 If-None-Match 首部發給服務器。服務器發現自己的資源 ETag 值并沒有發生改變,于是直接響應 304 。客戶端收到 304 后,直接使用緩存的 app.js ,同時更新緩存有效期。
客戶端再次發起請求 app.js 時,把之前保存的 ETag 值放入 If-None-Match 首部發給服務器。服務器發現自己的資源 ETag 值已經發生改變,于是響應 200 ,將修改后的 app.js 和新的 ETag 發送給客戶端。客戶端收到 200 后,重新下載新的 app.js ,并把新的 app.js 和響應首部緩存起來,替換原先的舊緩存。
當客戶端本地存儲有多個版本的資源時,會把所有的實體標簽都上傳,形如 ETag: "abc","def" ,服務端會使用 ETag 首部返回匹配中的實體標簽值。
實體標簽分為強標簽(Strong ETag)和弱標簽(Weak ETag),弱標簽以 W/ 開頭,如 ETag: W/"1234" 。強標簽使用強比較,弱標簽使用弱比較。強比較意味著兩個比較對象的每一個字節都相同,弱比較意味著兩者語義相同(Semantic Equivalence)。舉個栗子,假如響應首部包含一個渲染時間 Rendered-Time,A 響應的渲染時間為 365,B 響應的渲染時間為 345,兩個響應的實體內容一致。這種情況下,我們可以說 A 和 B 弱比較相等,強比較不相等。
一般來說,靜態內容使用強標簽,動態生成的內容使用弱標簽。
由此可以看出,實體首部可以解決一些 Last-Modified 無法解決的問題:
某些服務器不能得到文件的精確的最后修改時間
修改時間變了并不意味著內容的改變,比如改完保存后又改回去
修改時間只能精確到秒,一秒內的修改無法判斷
If-Match 和 ETag 的另一種用法:避免“空中碰撞”,以防編輯沖突。當客戶端使用 PUT 或者 POST 更新服務端資源時,需要使用 If-Match 來攜帶實體標簽給服務端,以確保客戶端要修改的資源沒有被別人修改過,避免覆蓋別人的修改。不過這種用法比較少,可以不用深究。
ExpiresExpires 指明資源的過期時間,如 Expires: Wed, 04 Jul 2012 08:26:05 GMT 。非法的日期格式(如 0)將會被當做過去的時間,表示該資源已經過期。
如果 Expires 和 Cache-Control 的 max-age 或者 s-maxage 同時出現,Expires 將被忽略。
AgeAge 表示資源在代理服務器上已經緩存了多久時間,單位為秒。如果是 Age: 0 ,表明該資源剛剛從服務器獲取。它的計算方式一般使用代理服務器當前的時間減去緩存資源的 Date 時間。
PragmaPragma 是 HTTP/1.0 中引入的首部,現在使用時一般用于向后兼容 HTTP/1.0,不鼓勵使用。
Pragma: no-cache 的作用與 Cache-Control: no-cache 一致,表示需要跟服務器進行驗證后才能使用緩存資源。
啟發式緩存策略并不是每個服務器都會返回明確的緩存策略,這種情況下客戶端會采取啟發式緩存策略。注意,只有在服務端沒有返回明確的緩存策略時才會激活啟發式緩存策略。
啟發式緩存策略會根據其他的首部信息來計算一個過期時間,其他的首部通常是 Date 和 Last-Modified 。此時,緩存有效期一般取兩者差值的 10% 。
使用啟發式緩存策略時,如果超過當前時間 24 小時且從未警告過,瀏覽器或者代理服務器應該在響應中產生一個警告首部字段 Warning: 113 。
參考資料HTTP/1.0, RFC1945
HTTP/1.1, RFC2616
HTTP/1.1 Caching, RFC7323
HTTP 緩存, Google Developers
The-difference-between-strong-and-weak-ETags
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/61963.html
摘要:的主要組件包含了一個全新的引擎,稱為量子,也稱為。這個新引擎集成了四種不同瀏覽器的最新創新技術,創造出一個全新的超級引擎。這可以發生在多個圖層上。最終,擁有最高特異性的規則會勝出。 原文:Inside a Super Fast CSS Engine: Quantum CSS(Aka Stylo), Lin Clark 注:原文發布于 2017 年 8 月,本文翻譯于 2018 年 4 ...
摘要:性能簡史在年,被創造出來時并不是沖著性能去的。而且在之后的十年發展中,它的性能一直是很低的。的引入成就了性能提升的一個轉折點,其執行速度比以往快了之多。性能提升也使得在全新的問題上使用成為可能。現在,極可能是下一個性能轉折點。 你可能已經聽說 WebAssembly 代碼跑起來非常快。但是你知道這是為什么嗎?在本系列文章中,我們將探究其原因。 何為 WebAssembly WebAss...
摘要:我們還經驗性地演示了貝葉斯在語言建模基準和生成圖說任務上優于傳統,以及通過使用不同的訓練方案,這些方法如何改進我們的模型。第節和第節分別回顧了通過反向傳播做貝葉斯,和通過時間做反向傳播。 摘要在這項工作里,我們探討了一種用于 RNN 的簡單變分貝葉斯方案(straightforward variational Bayes scheme)。首先,我們表明了一個通過時間截斷反向傳播的簡單變化,能...
摘要:為了更好的理解,我們有必要去先理解什么是匯編,以及編譯器是如何產生匯編的。什么是匯編現在,我們來看看外星人的大腦是如何工作的。這些注釋就是匯編,也稱為符號機器碼。結束以上的內容就是什么是匯編以及它是如何從高級編程語言翻譯過來的。 本文是圖說 WebAssembly 系列文章的第三篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 為了更好的理解 WebAssembly ,我們有必要去先...
閱讀 2321·2021-11-24 10:18
閱讀 3385·2021-09-22 15:35
閱讀 3339·2021-09-13 10:37
閱讀 3766·2021-09-06 15:14
閱讀 2071·2021-09-06 15:02
閱讀 2211·2021-09-02 15:11
閱讀 546·2019-08-30 15:53
閱讀 3075·2019-08-29 16:15