HTTP緩存機制一二三

web緩存大致可以分爲:數據庫緩存,服務器端緩存(代理服務器緩存,CDN緩存)、瀏覽器緩存。

瀏覽器緩存也包含很多內容:HTTP緩存、indexDB緩存、cookie、localstorage等等。這裏我們只討論HTTP緩存相關內容。

在具體瞭解HTTP緩存之前先來明確幾個術語:

  • 緩存命中率:從緩存中得到數據的請求數與所有請求數的比率。理想狀態是越高越好。
  • 過期內容:超過設置的有效事件,被標記爲”陳舊”的內容。通常過期內容不能用於回覆客戶端的請求,必須重新向源服務器請求新的內容或者驗證緩存的內容是否仍然準備。
  • 驗證:驗證緩存中的過期內容是否仍然有效,驗證通過的話重新刷新過期時間。
  • 失效:失效就是把內容從緩存中移除。當內容發生改變時就必須移除失效的內容。

    瀏覽器緩存主要是HTTP協議定義的緩存機制。HTML meta標籤,例如

<meta http-equiv="Pragma" content="no-store">

含義是讓瀏覽器不緩存當前頁面。但是代理服務器不解析HTML內容,一般應用廣泛的是用HTTP頭信息控制緩存。

HTTP 頭信息控制緩存

大致分爲兩種:強緩存和協商緩存。強緩存如果命中緩存不需要和服務器端發生交互,而協商緩存不管是否命中都要和服務器端發生交互,強制緩存的優先級高於協商緩存。具體內容下文介紹。

匹配流程(已有緩存的情況下):
這裏寫圖片描述

強緩存

可以理解爲無須驗證的緩存策略。對強緩存來說,響應頭中有兩個字段Expires/Cache-Control來表明規則。

Expires

Expires是指緩存過期的時間,超過這個時間點就代表資源過期。有一個問題是由於使用具體時間,如果時間表示出錯或者沒有轉換到正確的時區都可能造成緩存生命週期出錯。並且Expires是HTTP/1.0的標準,現在更傾向於用HTTP/1.1中定義的Cache-Control。兩個同時存在時也是Cache-Control的優先級更高。

Cache-Control

Cache-Control可以由多個字段組合而成,主要有以下幾個取值:

1.max-age指定一個時間長度,在這個時間段內緩存是有效的,單位是s。例如設置Cache-Contro : max-age=31536000,也就是說緩存有效期爲(31536000/24/60*60)天,第一次訪問這個資源的時候,服務器端也返回了Expires字段,並且過期時間是一年後。

這裏寫圖片描述

在沒有禁用緩存並且沒有超過有效時間的情況下,再次訪問這個資源命中了緩存,不會向服務器請求資源而是直接從瀏覽器緩存中取。

這裏寫圖片描述

2. s-maxage同max-age,覆蓋max-age、Expires,但僅適用於共享緩存,在私有緩存中被忽略。

3.public表明響應可以被任何對象(發送請求的客戶端、代理服務器等等)緩存。
4.private 表明響應只能被單個用戶(可能是操作系統用戶、瀏覽器用戶)緩存,是非共享的,不能被代理服務器緩存。
5.no-cahce強制所有緩存了該響應的用戶,在使用已緩存的數據前,發送帶驗證器的請求到服務器。不是字面意思上的不緩存。
6.no-store 禁止緩存,每次請求都要向服務器重新獲取數據

協商緩存

緩存的資源到期了,並不意味着資源內容發生了改變,如果和服務器上的資源沒有差異,實際上沒有必要再次請求。客戶端和服務器端通過某種驗證機制驗證當前請求資源是否可以使用緩存。

瀏覽器第一次請求數據之後會將數據和響應頭部的緩存標識存儲起來。再次請求時會帶上存儲的頭部字段,服務器端驗證是否可用。如果返回304 Not Modified,代表資源沒有發生改變可以使用緩存的數據,獲取新的過期時間。反之返回200就相當於重新請求了一遍資源並替換舊資源。
Last-modified/If-Modified-Since
Last-modified:服務器端資源的最後修改時間,響應頭部會帶上這個標識。第一次請求之後,瀏覽器記錄這個時間,再次請求時,請求頭部帶上If-Modified-Since即爲之前記錄下的時間。服務器端收到帶If-Modified-Since的請求後會去和資源的最後修改時間對比。若修改過就返回最新資源,狀態碼200,若沒修改過則返回304。

這裏寫圖片描述

注意:如果響應頭中有Last-modified而沒有Expire或Cache-Control
時,瀏覽器會有自己的算法來推算出一個時間緩存該文件多久,不同瀏覽器得出的時間不一樣,所以Last-modified要記得配合Expires/Cahce-Control使用。

Etag/If-None-Match

由服務器端生成的一段hash字符串,第一次請求時響應頭帶上ETag:abcd,之後的請求中帶上If-None-Match:abcd,服務器檢查ETag,返回304或200。

這裏寫圖片描述

關於last-modified和Etag區別,已經有很多人總結過了:

  • 某些服務器不能精確得到資源的最後修改時間,這樣就無法通過最後修改時間判斷資源是否更新。
  • last-modified只能精確到秒。
  • 一些資源的最後修改時間改變了,但是內容沒改變,使用Last-modified看不出內容沒有改變。
  • Etag的精度比Last-modified高,屬於強驗證,要求資源字節級別的一致,優先級高。如果服務器端有提供ETag的話,必須先對ETag進行Conditional Request。

    注意:實際使用ETag/Last-modified要注意保持一致性,做負載均衡和反向代理的話可能會出現不一致的情況。計算ETag也是需要佔用資源的,如果修改不是過於頻繁,看自己的需求用Cache-Control是否可以滿足。

這裏寫圖片描述

實際應用

回到實際應用上來,首先要明確哪些內容適合被緩存哪些不適合。
考慮緩存的內容:

  • css樣式文件
  • js文件
  • logo、圖標
  • html文件
  • 可以下載的內容

    一些不應該被緩存的內容

  • 業務敏感的GET請求

    可緩存的內容又分爲幾種不同的情況:
    不經常改變的文件:

    給max-age設置一個較大的值,一般設置max-age=31536000

比如引入的一些第三方文件、打包出來的帶有hash後綴的css、js文件。一般來說文件內容改變了,會更改版本號、hash值。相當於請求另一個文件。

標準中規定max-age的值最大不超過一年,所以設成max-age=31536000。至於過期內容,緩存區會將一段時間沒有使用的文件刪除掉。

有看到用對話的形式來描述這個過程,便仿照着試圖更清晰地解釋:

這裏寫圖片描述

這裏寫圖片描述

可能經常需要變動的文件:
Cache-Control: no-cahce/max-age=0

比如入口index.html文件、文件內容改變但名稱不變的資源。選擇Etag或last-modified來做驗證,在使用緩存資源之前一定會去服務器做驗證,命中緩存時會比第一種情況慢一點點,畢竟還要發請求通信。
這裏寫圖片描述
這裏寫圖片描述

注意: 這裏只描述了最基本的思路,實際使用 HTTP 緩存需要後端配合配置,具體情況具體對待,而且各方的實現並不一定完全按照標準來的,踩踩坑更健康��。

參考文章

https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn#publicprivate
https://jakearchibald.com/2016/caching-best-practices
https://zhuanlan.zhihu.com/p/28113197
https://www.digitalocean.com/community/tutorials/web-caching-basics-terminology-http-headers-and-caching-strategies
https://stackoverflow.com/questions/12908766/what-is-cache-control-private
http://www.alloyteam.com/2016/03/discussion-on-web-caching/#prettyPhoto

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章