淺談HTTP緩存策略

1.web緩存

????緩存的作用就是提升網頁加載速度。瀏覽器加載一個完整的網頁勢必會引用外部資源(圖片,js,css)。若每次加載網頁都要去加載這些外部資源則會引起不必要的時間和資源浪費,且會影響用戶體驗。而解決上述問題需要一個優秀的緩存策略。除此之外,web緩存的優點還有很多,例如:減輕服務器壓力 ,加快了客戶端加載速度,節省網絡帶寬等。
web緩存按緩存位置,緩存機制可大致分為三類。

  • 1 數據庫緩存。
  • 2 服務器緩存。
  • 3 客戶端(瀏覽器)緩存/HTTP緩存。
    下面著重介紹HTTP緩存。

2.HTTP緩存

????HTTP緩存就是將靜態資源存儲在客戶端本地,下次請求該資源時直接使用本地資源,而不必從服務端加載。而當服務端的靜態資源更新時,本地緩存資源也要更新。當然這需要一系列策略進行約定。

2.1 強緩存策略

所謂強緩存策略即在靜態資源有效期內,使用該資源時直接使用本地資源,不請求服務器。

2.1.1 HTTP1.0 expires

????在HTTP1.0中,服務器使用expires字段規定過期時間,可以在客戶端資源請求的Response Headers 中增加 expires 字段表示資源的過期時間。它是一個時間戳,當客戶端再次請求該資源的時候,會把客戶端時間與該時間戳進行對比,如果大于該時間戳則已過期,否則直接使用該緩存資源。下面用一個實例驗證,使用node.js搭建一個web服務,使用定時器創建一個隨時間變化的內容。

const http = require("http");
let time = null;
function updateTime() {
  setInterval(() => (time = new Date().toUTCString()), 1000);
}
updateTime();
// time每5秒更新一次
http
  .createServer((req, res) => {
    const { url } = req;
    if ("/" === url) {
      res.end(`
            <html>
                <!-- <meta charset="UTF-8"> -->
                Html Update Time: ${time}
                <script src='main.js'></script>
            </html>
            `);
    } else if (url === "/main.js") {
      const content = `document.writeln('<br>JS Update Time:${time}')`;
      // HTTP1.0 強緩存
      res.setHeader('Expires', new Date(Date.now() + 5 * 1000).toUTCString())
      res.statusCode = 200;
      res.end(content);
    } else if (url === "/favicon.ico") {
      res.end("");
    }
  })
  .listen(3000, () => {
    console.log("服務已啟動" + 3000);
  });

????上面代碼實現的效果是,每次刷新頁面,HTML時間一秒鐘變化一次,而JS時間由于設置了強緩存因此每5秒變化一次。可以訪問http://localhost:3000進行驗證。

強緩存驗證.jpg

????HTML時間和JS時間不一致,說明強緩存生效。但這種強緩存方式存在一些問題,由于發送請求時是使用客戶端時間進行對比,因此一方面是客戶端和服務端時間可能不一致,另一方面是客戶端的時間(系統時間)是可以自行修改的,因此可能出現服務器資源與本地緩存資源不一致的問題。

2.1.2 HTTP1.1 cache-control

????鑒于使用expires字段可能出現的問題,HTTP1.1 新增了 cache-control 字段來解決該問題,所以當 cache-control 和 expires 都存在時,cache-control 優先級更高。cache-control 主要有 max-age 和 s-maxage、public 和 private、no-cache 和 no-store 等值。max-age字段是一個時間長度,單位秒,表示該資源過了多少秒后失效。當客戶端請求資源的時候,發現該資源還在有效時間內則使用該緩存,它不依賴客戶端時間。

.png

下面使用該字段設置強緩存

// HTTP1.0 強緩存
res.setHeader('Expires', new Date(Date.now() + 5 * 1000).toUTCString())
// HTTP1.1 強緩存 cache-control字段   cache-control 優先級更高
res.setHeader('Cache-Control', 'max-age=10')
強緩存驗證.jpg

可以看到強緩存生效且cache-control字段優先級比expires高。
鑒于上述強緩存的特點,強緩存的應用場景一般是用于需要定期更新的內容。

2.2 協商緩存

????上面所述的強緩存會直接訪問本地緩存,沒過期的話不會請求服務器,直接使用本地緩存。而協商緩存,顧名思義,需要和服務器協商一下,看資源是否需要更新。若協商結果是需要更新則會返回更新的內容。若結果是不需要則只返回304狀態碼,這樣可以有效減輕服務器壓力。協商緩存的方式主要有以下兩種。

2.2.1 last-modified & if-Modified-Since

該方式的協商過程為:
1 服務器進行靜態資源應答時會通過last-modified字段來標示修改時間。
2 瀏覽器下次請求相同資源會將last-modified時間作為if-modified-since字段的值放在請求報文中用以詢問服務器是否該資源過期。
3 服務器需要通過規則判斷是否過期。
4 過期時直接返回200并在body中放入更新內容。
5 如果未過期則直接返回304狀態碼。
下面進行實例驗證

 // 協商緩存
 // 方式一 last-modified & if-Modified-Since  通過協商修改時間為基礎的策略
 // 首先需要禁用強緩存
 res.setHeader('Cache-Control', 'no-cache') 
 res.setHeader('last-modified', new Date().toUTCString())
 // 設置過期時間為5秒
 if (new Date(req.headers['if-modified-since']).getTime() + 5 * 1000 > Date.now()) {
        console.log('協商緩存命中....')
        res.statusCode = 304
        res.end()
        return
  }
協商緩存驗證.jpg

協商緩存命中.jpg
2.2.2 etag & if-None-Match

????第二種方式是通過內容判斷,一般的做法是將返回內容使用Hash函數進行消息摘要,然后通過對比摘要來判斷內容是否需要更新。其協商過程為:
1 服務器進行靜態資源應答時通過etag來標示內容摘要。
2 瀏覽器下次請求相同資源會將etag作為if-none-match字段放在請求報文中用以詢問服務器是否該資源過期。
3 服務器需要通過和服務器內容的摘要進行比對確定是否過期。
4 過期時直接返回200并在body中放入更新內容。
5 如果未過期則直接返回304狀態碼。
下面進行實例驗證

    res.setHeader("Cache-Control", "no-cache");
    const crypto = require("crypto"); // nodejs的一個加密模塊
    // createHash 創建并返回一個 Hash 對象,該對象可用于生成哈希摘要  digest 字符編碼
    const hash = crypto.createHash("sha1").update(content).digest("hex");
    res.setHeader("Etag", hash);
    if (req.headers["if-none-match"] === hash) {
      console.log("Etag協商緩存命中.....");
      res.statusCode = 304;
      res.end();
       return;
    }
Etag協商緩驗證.jpg

Etag協商緩命中.jpg

????可以看到由于該策略是通過判斷內容來決定是否需要更新,因此HTML時間和JS時間會保持一致。因為時間每秒更新一次因此在一秒內刷新頁面時會命中協商緩存。因此鑒于協商緩存的特點,其一般用于非定期更新的內容,需要客戶端發送請求詢問服務器是否需要更新。

參考:https://www.josephxia.com/document/node/cache/HTTP%E7%BC%93%E5%AD%98.html#web%E7%BC%93%E5%AD%98%E6%98%AF%E4%BB%80%E4%B9%88

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370