深入解析 HTTP 快取控制

huansky發表於2021-02-21

快取(Cache)是計算機領域裡的一個重要概念,是優化系統效能的利器。

由於鏈路漫長,網路時延不可控,瀏覽器使用 HTTP 獲取資源的成本較高。所以,非常有必要把“來之不易”的資料快取起來,下次再請求的時候儘可能地複用。這樣,就可以避免多次請求 - 應答的通訊成本,節約網路頻寬,也可以加快響應速度。

試想一下,如果有幾十 K 甚至幾十 M 的資料,不是從網路而是從本地磁碟獲取,那將是多麼大的一筆節省,免去多少等待的時間。

實際上,HTTP 傳輸的每一個環節基本上都會有快取,非常複雜。

快取策略

在闡述HTTP不同快取策略之前,我們需要知道使用者重新整理/訪問行為 的手段分成三類:

  • 在URI輸入欄中輸入然後回車/通過書籤訪問

  • F5/點選工具欄中的重新整理按鈕/右鍵選單重新載入

  • Ctl+F5 (完全不使用HTTP快取)

不同的重新整理手段,會導致瀏覽器使用不同的快取策略,我們下面會分析到HTTP 快取主要是通過請求和響應報文頭中的對應 Header 資訊,來控制快取的策略。

HTTP快取的型別很多,根據是否需要重新向伺服器發起請求來分類包括兩種:強制快取 和 對比快取。

http快取請求相應頭

先大概瞭解這些快取欄位是必須的,後面也會細說,先留個印象。

1. Cache-Control

請求/響應頭,快取控制欄位,可以說是控制http快取的最高指令,要不要快取也是它說了算。

它有以下常用值

  • no-store:所有內容都不快取(這個才是響應不被快取的意思)。

  • no-cache:快取,但是瀏覽器使用快取前,都會請求伺服器判斷快取資源是否是最新,它是個比較高貴的存在,因為它只用不過期的快取。

  • max-age=x(單位秒) :請求快取後的X秒不再發起請求,屬於http1.1屬性,與下方Expires(http1.0屬性)類似,但優先順序要比Expires高。

  • s-maxage=x(單位秒):代理伺服器請求源站快取後的X秒不再發起請求,只對CDN快取有效(這個在後面會細說)

  • public: 客戶端和代理伺服器(CDN)都可快取

  • private: 只有客戶端可以快取

2. Expires

響應頭,代表資源過期時間,由伺服器返回提供,GMT格式日期,是http1.0的屬性,在與max-age(http1.1)共存的情況下,優先順序要低。

3. Last-Modified

響應頭,資源最新修改時間,由伺服器告訴瀏覽器。

4. if-Modified-Since

請求頭,資源最新修改時間,由瀏覽器告訴伺服器(其實就是上次伺服器給的Last-Modified,請求又還給伺服器對比),和Last-Modified是一對,它兩會進行對比

5. Etag

響應頭,資源標識,由伺服器告訴瀏覽器。主要是用來解決修改時間無法準確區分檔案變化的問題。

6. if-None-Match

請求頭,快取資源標識,由瀏覽器告訴伺服器(其實就是上次伺服器給的Etag),和Etag是一對,它兩會進行對比

7. s-maxage

(單位為s):同max-age作用一樣,只在代理伺服器中生效(比如CDN快取)。比如當s-maxage=60時,在這60秒中,即使更新了CDN的內容,瀏覽器也不會進行請求。max-age用於普通快取,而s-maxage用於代理快取。s-maxage的優先順序高於max-age。如果存在s-maxage,則會覆蓋掉max-age和Expires header。

8. max-stale

能容忍的最大過期時間。max-stale 指令標示了客戶端願意接收一個已經過期了的響應。如果指定了max-stale的值,則最大容忍時間為對應的秒數。如果沒有指定,那麼說明瀏覽器願意接收任何age的響應(age表示響應由源站生成或確認的時間與當前時間的差值)。

9. min-fresh

min-fresh 的意思是快取必須有效,而且必須在 x 秒後依然有效。比如“min-fresh=1”,這是絕對不允許過期的。這時有個資源在快取裡面已經存了7s了,其中 “max-age=10”,那麼“7+1<10”,在 1s 之後還是新鮮的,因此是有效的。

強制快取

在瀏覽器已經快取資料的情況下,使用強制快取去請求資料的流程是這樣的:

從流程圖可以看到,強制快取,在快取資料未失效的情況下,可以直接使用快取資料,不需要再請求伺服器,那麼瀏覽器是如何判斷快取資料是否失效呢?
對於強制快取來說,響應 header 中會有兩個欄位來標明失效規則(Expires/Cache-Control):

  • Expires:

Expires 是HTTP1.0的產物了,現在預設瀏覽器均預設使用HTTP 1.1,所以它的作用基本忽略。但是很多網站還是對它做了相容。它的值為服務端返回的到期時間,即下一次請求時,請求時間小於服務端返回的到期時間,直接使用快取資料。

但有一個問題是到期時間是由服務端生成的,如果客戶端時間跟伺服器時間不一致,這就會導致快取命中的誤差。

在HTTP 1.1 的版本,Expires被Cache-Control替代。

  • Cache-Control:

Cache-Control 是最重要的規則。常見的取值前面已經描述過,此處不再詳細描述。

舉個板栗

圖中 Cache-Control 僅指定了max-age,所以預設為 private,快取時間為 31536000秒(365天)
也就是說,在 365 天內再次請求這條資料,都會直接獲取快取資料庫中的資料,直接使用。

對比快取(協商快取)

對比快取,顧名思義,需要進行比較判斷是否可以使用快取。

瀏覽器第一次請求資料時,伺服器會將快取標識與資料一起返回給客戶端,客戶端將二者備份至快取資料庫中。

再次請求資料時,客戶端將備份的快取標識傳送給伺服器,伺服器根據快取標識進行判斷,判斷成功後,返回304狀態碼,通知客戶端比較成功,可以使用快取資料。

第一次訪問:

再次訪問:

通過兩圖的對比,我們可以很清楚的發現,在對比快取生效時,狀態碼為304,並且報文大小和請求時間大大減少。

原因是,服務端在進行標識比較後,只返回header部分,通過狀態碼通知客戶端使用快取,不再需要將報文主體部分返回給客戶端。

對於對比快取來說,快取標識的傳遞是我們著重需要理解的,它在請求header和響應header間進行傳遞,一共分為兩種標識傳遞,接下來,我們分開介紹。

  • Last-Modified / If-Modified-Since

伺服器響應請求時,會通過 Last-Modified HTTP 頭告訴瀏覽器資源的最後修改時間,瀏覽器本地對資源快取起來,之後再請求的時候,會帶上一個HTTP頭If-Modified-Since,這個值就是伺服器上一次給的Last-Modified的時間,伺服器會拿著瀏覽器傳過來的時間比對資源當前最後的修改時間,如果大於If-Modified-Since,則說明資源修改過了,瀏覽器不能再使用快取,伺服器重新一份完整的資源瀏覽器,否則瀏覽器可以繼續使用快取,並返回304狀態碼

  • Etag / If-None-Match(優先順序高於Last-Modified / If-Modified-Since)

伺服器響應請求時,通過 Etag HTTP 頭部告訴瀏覽器當前資源在伺服器的唯一標識(生成規則由伺服器決定),瀏覽器再次請求時,就會帶上一個頭If-None-Match,這個值就是伺服器上一次給的 Etag 的值,伺服器比對一下資源當前的Etag是否跟If-None-Match一致,不一致則說明資源修改過了,瀏覽器不能再使用快取,否則瀏覽器可以繼續使用快取,並返回304狀態碼

ETag 還有“強”“弱”之分。強 ETag 要求資源在位元組級別必須完全相符,弱 ETag 在值前有個“W/”標記,只要求資源在語義上沒有變化,但內部可能會有部分發生了改變(例如 HTML 裡的標籤順序調整,或者多了幾個空格)。

“ETag: W/"v2.6"
If-None-Match: W/"v2.6"

不管相關的實體值以何種方式發生了變化,強實體標籤都要發生變化。而相關實體在語義上發生了比較重要的變化時,弱實體標籤也應該發生變化。”

值得注意的是:Etag 的校驗優先順序高於 Last-Modified

什麼時候應該使用 Etag 和 Last-Modified 

如果伺服器回送了一個實體標籤,HTTP/1.1 客戶端就必須使用實體標籤驗證器。如果伺服器只回送了一個 Last-Modified 值,客戶端就可以使用 If-Modified-Since 驗證。如果實體標籤和最後修改日期都提供了,客戶端就應該使用這兩種再驗證方案,這樣 HTTP/1.0 和 HTTP/1.1 快取就都可以正確響應了。

除非 HTTP/1.1 原始伺服器無法生成實體標籤驗證器,否則就應該傳送一個出去,如果使用弱實體標籤有優勢的話,傳送的可能就是個弱實體標籤,而不是強實體標籤。而且,最好同時傳送一個最近修改值。

如果 HTTP/1.1 快取或伺服器收到的請求既帶有 If-Modified-Since,又帶有實體標籤條件首部,那麼只有這兩個條件都滿足時,才能返回 304 Not Modified 響應。

瀏覽器行為

對瀏覽器的不同行為對快取的影響做一個總結。

  1. 瀏覽器位址列回車,或者點選跳轉按鈕,前進,後退,新開視窗,這些行為,會讓Expires,max-age生效,也就是說,這幾種操作下,瀏覽器會判斷過期時間,再考慮要不要發起請求,當然Last-Modified和Etag也有效。

  2. F5重新整理瀏覽器,或者使用瀏覽器導航欄的重新整理按鈕,這幾種,會忽略掉Expires,max-age的限制,瀏覽器會在請求頭裡加一個“Cache-Control: max-age=0” 強行發起請求,它可以配合 ETag 和 Last-Modified 使用,如果本地快取還在,且伺服器返回 304 ,依然可以使用本地快取。

  3. CTRL+F5是強制請求,它其實是發了一個“Cache-Control: no-cache”,含義和“max-age=0”基本一樣,就看後臺的伺服器怎麼理解,通常兩者的效果是相同的。

但事實上,我們很少用到位址列回車,位址列跳轉,所以要觸發快取時間的判斷,還需要特定的操作,站在我的理解,綜合考慮下,才有了這麼多網站的cache-control設定為no-cache,也就是使用快取前都判斷檔案是否為最新,更為合理。 

快取和廣告

讀到這裡,你一定已經意識到快取可以提高效能並減少流量。知道快取可以幫助使用者,併為使用者提供更好的使用體驗,而且快取也可以幫助網路運營商減少流量。

釋出廣告者的兩難處境

你可能認為內容提供商會喜歡快取。畢竟,如果到處都是快取的話,內容提供商就不需要購買大型的多處理器 Web 伺服器來滿足使用者需求了——他們不需要付過高的網路服務費,一遍一遍地向使用者傳送同樣的資料。更好的一點是,快取可以將那些漂亮的文章和廣告以更快,甚至更好看的方式顯示在使用者的顯示器上,鼓勵他們去瀏覽更多的內容,看更多的廣告。這就是內容提供商所希望的!吸引更多的眼球和更多的廣告!

但這就是困難所在。很多內容提供商的收益都是通過廣告實現的——具體來說,每向使用者顯示一次廣告內容,內容提供商就會得到相應的收益。(可能還不到一兩便士,但如果一天顯示數百萬條廣告的話,這些錢就會疊加起來!)這就是快取的問題——它們會向原始伺服器隱藏實際的訪問次數。如果快取工作得很好,原始伺服器可能根本收不到任何 HTTP 訪問,因為這些訪問都被因特網快取吸收了。但如果你的收益是基於訪問次數的話,你就高興不起來了。

釋出者的響應

現在,廣告商會使用各種型別的“快取清除”技術來確保快取不會竊取他們的命中流量。他們會在內容上加上 no-cache 首部。他們會通過 CGI 閘道器提供廣告。還會在每次訪問時重寫廣告 URL。
這些快取清除技術並不僅用於代理快取。實際上,現在主要將其用於每個 Web 瀏覽器中都啟用了的快取。但是,如果某些內容提供商維護其命中率的行為太過火了,就會降低快取為其站點帶來的積極作用。

理想情況下,內容提供商會讓快取吸收其流量,而快取會告訴內容提供商它們攔截了多少次命中。現在,快取有好幾種方式可以做到這一點。

一種解決方案就是配置快取,每次訪問時都與原始伺服器進行再驗證。這樣,每次訪問時都會將命中推向原始伺服器,但通常不會傳送任何主體資料。當然,這樣會降低事務處理的速度。

日誌遷移

理想的解決方案是不需要將命中傳遞給伺服器的。畢竟,快取就可以記錄下所有的命中。快取只要將命中日誌傳送給伺服器就行了。實際上,為了保持內容提供商們 的滿意度,有些大型快取的提供商已經在對快取日誌進行人工處理,並將其傳送給受影響的內容提供商了。

但是,命中日誌很大,很難移動。而快取日誌並沒有被標準化或被組織成獨立的日誌,以傳送給單獨的內容提供商。而且,這裡面還存在著認證和隱私問題。

已經有一些高效(和不那麼高效的)日誌分發策略的建議了。但還沒有哪個建議成熟到足以為 Web 軟體廠商採用。很多建議都非常複雜,需要聯合商業夥伴才能實現。有幾家聯合廠商已經開始開發廣告收入改造工程的支撐框架了。

命中計數和使用限制

RFC 2227,“HTTP 的簡單命中計數和使用限制”中定義了一種簡單得多的方案。這個協議向 HTTP 中新增了一個稱為 Meter 的首部,這個首部會週期性地將對特定 URL 的命中次數回送給伺服器。通過這種方式,伺服器可以從快取週期性地獲取對已快取文件命中次數的更新。

而且,伺服器還能控制在快取必須向伺服器彙報之前,其中的文件還可以使用多少次,或者為快取文件設定一個時鐘超時值。這種控制方式被稱為使用限制;通過這種方式,伺服器可以對快取向原始伺服器彙報之前,已快取資源的使用次數進行控制。

總結

對於強快取,伺服器通知瀏覽器一個快取時間,在快取時間內,下次請求,直接用快取,不在時間內,執行對比快取策略。

對於對比快取,將快取資訊中的Etag和Last-Modified通過請求傳送給伺服器,由伺服器校驗,返回304狀態碼時,瀏覽器直接使用快取。

瀏覽器第一次請求:

瀏覽器再次請求時:

  

 

參考文章

http快取詳解,http快取推薦方案

5分鐘看懂系列:HTTP快取機制詳解

透視HTTP協議 

徹底弄懂 HTTP 快取機制及原理!

相關文章