HTTP緩存

前言

一直以來都對瀏覽器緩存很感興趣,但老是隻知其一;不知其二,只是很碎片化的瞭解一些相關知識,此次就打算把全部的碎片整合一下,基於我目前的理解整理一份總結。等之後認識的更全面了,再逐步添加修改。node

瀏覽器緩存的做用

合理使用瀏覽器的緩存,能夠有效的減小請求的訪問次數,一個是減少服務器壓力,更重要的是可以下降頁面的加載時間。算法

HTTP緩存的幾種形式及實現

Pragma

這是一種比較古老的請求頭,只建議在向下兼容http1.0的時候使用,當值爲Pragma:no-cache,效果和Cache-Control:no-cache同樣。瀏覽器

res.setHeader('Pragma', 'no-cache');
複製代碼

以nodejs爲例,文件每次都會向服務器從新發起請求,哪怕設置了其餘緩存方式,具體優先級待會會詳細介紹。緩存

Expires

Exprires也是兼容http1.0的一項請求頭。bash

經過服務端設置,給Response Headers添加一個GMT(格林尼治時間)做爲Express的響應頭,客戶端在發請求前,判斷Express的時間是否已通過期來決定是否向服務端發送請求。服務器

// 過時時間在當前時間下添加5秒
const expires = new Date(Date.parse(new Date())+5000);
res.setHeader("Expires", expires.toUTCString());
複製代碼

其中Date爲上一次請求的時間異步

因爲客戶端在作判斷的時候取的是本身電腦的時間,因此當用戶的電腦時間出現問題時,會影響到緩存的正常使用。性能

Cache-Control

當http發展到1.1時,簡單的請求頭已經沒法覆蓋全部的操做,因此須要更加靈活的Cache-Control來充實瀏覽器緩存的形式。spa

客戶端能夠在HTTP請求中使用的標準Cache-Control代理

//告訴服務端,願意接收一個請求時間爲seconds秒的資源
Cache-Control: max-age=<seconds>
//告訴服務端,願意接收一個超出緩存時間seconds秒的資源,若是未定義,則容許超出任意時間
Cache-Control: max-stale[=<seconds>]
//告訴服務端,但願接收一個seconds秒內被更新過的資源
Cache-Control: min-fresh=<seconds>
//告訴服務端,不直接使用緩存,跳過強緩存的步驟
Cache-control: no-cache
//告訴服務端,全部內容都不被緩存到瀏覽器中
Cache-control: no-store
//告訴服務端,但願獲取實體數據沒有被轉換過(如壓縮)的資源
Cache-control: no-transform
//告訴服務端,但願獲取緩存的內容(如有)
Cache-control: only-if-cached
複製代碼

服務器能夠在響應中使用的標準Cache-Control

//緩存必須在使用以前驗證舊資源的狀態,而且不可以使用過時資源
Cache-control: must-revalidate
//不直接使用緩存,跳過強緩存的步驟
Cache-control: no-cache
//全部內容都不被緩存到瀏覽器中
Cache-control: no-store
//不得對資源進行轉換或轉變。Content-Encoding, Content-Range, Content-Type等HTTP頭不能由代理修改。例如,非透明代理能夠對圖像格式進行轉換,以便節省緩存空間或者減小緩慢鏈路上的流量
Cache-control: no-transform
//代表響應能夠被任何對象(包括:發送請求的客戶端,代理服務器,等等)緩存
Cache-control: public
//代表響應只能被單個用戶緩存,不能做爲共享緩存(即代理服務器不能緩存它),能夠緩存響應內容
Cache-control: private
//與must-revalidate做用相同,但它僅適用於共享緩存(例如代理),並被私有緩存忽略
Cache-control: proxy-revalidate
//設置緩存存儲的最大週期,超過這個時間緩存被認爲過時(單位秒)。與Expires相反,時間是相對於請求的時間
Cache-Control: max-age=<seconds>
//覆蓋max-age 或者 Expires 頭,可是僅適用於共享緩存(好比各個代理),而且私有緩存中它被忽略
Cache-control: s-maxage=<seconds>
複製代碼

擴展Cache-Control指令(不是核心HTTP緩存標準文檔的一部分,有兼容問題)

//表示響應正文不會隨時間而改變。資源(若是未過時)在服務器上不發生改變,所以客戶端不該發送從新驗證請求頭(例如If-None-Match或If-Modified-Since)來檢查更新,即便用戶顯式地刷新頁面
Cache-control: immutable 
//代表客戶端願意接受陳舊的響應,同時在後臺異步檢查新的響應。秒值指示客戶願意接受陳舊響應的時間長度
Cache-control: stale-while-revalidate=<seconds>
//表示若是新的檢查失敗,則客戶願意接受陳舊的響應。秒數值表示客戶在初始到期後願意接受陳舊響應的時間
Cache-control: stale-if-error=<seconds>
複製代碼

下面是nodejs經過給Cache-Control添加了過時時間一個小時,每次加載文件時,客戶端會對Date進行比對,知道過時,不然不會向服務端發送新的請求

res.setHeader('Cache-Control', 'max-age=3600');
複製代碼

Cache-Control的比對比較複雜,是須要響應頭和請求頭同時做用的

(這裏關於響應頭和請求頭之間互相沖突時,客戶端是如何決定緩存結果的,我在後面再找時間添加)

Last-Modified

第一次請求資源時,會將資源最後更改的時間以Last-Modified: GMT的形式加在實體首部上一塊兒返回給客戶端,當再一次發起請求時,客戶端會帶上返回的GMT,賦值給If-Modified-Since,若服務端判斷時間沒有更改,則返回304 NOT MODIFIED

const stats = fs.statSync(pathname);
const lastModified = stats.mtime.toUTCString();
const ifModifiedSince = "If-Modified-Since".toLowerCase();
res.setHeader("Last-Modified", lastModified);
//校驗時判斷下當前文件的更新時間和請求返回的時間是否相同
if (
  req.headers[ifModifiedSince] &&
  lastModified == req.headers[ifModifiedSince]
) {
  //返回
  res.writeHead(304, "Not Modified");
}
複製代碼

服務端比對服務器中的文件和請求頭中的更新時間,發現時間一致,返回304,客戶端再也不從新更新文件。

PS:通常這時候不會立刻出現304的狀況,會發現瀏覽器根本沒有發送請求,而是直接拿了本地的緩存。

(此處內容是騰訊Bugly分享截取)這是由於瀏覽器的一種緩存過時策略。在沒有提供任何瀏覽器緩存過時策略的狀況下,瀏覽器遵循一個啓發式緩存過時策略:

根據響應頭中2個時間字段 Date 和 Last-Modified 之間的時間差值,取其值的10%做爲緩存時間週期。

如下完整的緩存策略三要素:

這裏須要注意的點,根據圖中的公式能夠看出,若是上次緩存的文件時間較長,加上僅僅配置了Last-Modified,有可能致使用戶一直沒法拿到最新的文件。

ETag

服務端在第一次請求資源時,會在響應頭ETag上添加一個由某種算符得出的隨機字符串,當客戶端再一次請求該項資源時,會將以前緩存的字符串,自動帶上給If-None-Match,服務端將獲得的字符和服務器中的進行對比。若是不一樣,則從新抓取文件,若相同,則返回304

//文件更新時從新設置etag(模擬服務器獲取etag)
const etag = '123456789abcdef';
const ifNoneMatch = "If-None-Match".toLowerCase();
res.setHeader("Etag", etag);
if (
  req.headers[ifNoneMatch] &&
  etag == req.headers[ifNoneMatch]
) {
  res.writeHead(304, "Not Modified");
}
複製代碼

不一樣緩存方式的優先級關係

我這裏就不寫詳細例子,直接將他們的優先級展現出來。

其中Pragma>Cache-Control>Exprise,優先級從左到右。

強緩存

服務器返回200 from cache屬於強緩存,客戶端經過Pragma、Cache-Control和Exprise判斷是否有緩存,若是有緩存,直接訪問本地的緩存,不訪問服務器,能更有效的節省資源。

磁盤緩存(from cache || from disk cache)

內存緩存(from memory cache)

協商緩存(弱緩存)

服務器返回304 Not Modified屬於協商緩存,客戶端經過向服務端發請求驗證資源是否過時,若發現已通過期,則從新拉去,若沒有過時,服務端返回304,客戶端則從本地緩存中獲取資源,不從新拉去。

分析不一樣緩存方式的優劣性

頭部字段 優點 劣勢
Pragma 基於http1.0,兼容性強 可操做性不強,基本已經再也不使用
Expires 基於http1.0,兼容性比較強,客戶端判斷是否過時,無需向服務端確認 1.因爲是基於用戶本地時間進行計算,因此可能存在不許確的問題。2.強緩存在到期以前用戶沒法知道資源是否過時。
Cache-Control 操做比較豐富,組合性強 一樣因爲是強緩存,可能存在更新不及時的問題
Last-Modified 兼容性強,更新及時 資源的任何變化都會改變動新時間,可能會重複加載徹底同樣的文件
ETag 文件的更新由服務端控制,操做性強,且兼容性強 計算ETag須要消耗性能,且不一樣的服務端計算的算法可能不一致,致使重複加載
相關文章
相關標籤/搜索