亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談HTTP 緩存的那些事兒

 更新時間:2018年10月17日 09:31:23   作者:PandaShen  
HTTP 緩存機(jī)制作為 Web 應(yīng)用性能優(yōu)化的重要手段,是想要成為前端架構(gòu)的必備技能。這篇文章主要介紹了淺談HTTP 緩存并用node實現(xiàn)緩存,非常具有實用價值,需要的朋友可以參考下

前言

HTTP 緩存機(jī)制作為 Web 應(yīng)用性能優(yōu)化的重要手段,對于從事 Web 開發(fā)的同學(xué)們來說,應(yīng)該是知識體系的基礎(chǔ)環(huán)節(jié),也是想要成為前端架構(gòu)的必備技能。

緩存的作用

我們?yōu)槭裁词褂镁彺?,是因為緩存可以給我們的 Web 項目帶來以下好處,以提高性能和用戶體驗。

  • 加快了瀏覽器加載網(wǎng)頁的速度;
  • 減少了冗余的數(shù)據(jù)傳輸,節(jié)省網(wǎng)絡(luò)流量和帶寬;
  • 減少服務(wù)器的負(fù)擔(dān),大大提高了網(wǎng)站的性能。

由于從本地緩存讀取靜態(tài)資源,加快瀏覽器的網(wǎng)頁加載速度是一定的,也確實的減少了數(shù)據(jù)傳輸,就提高網(wǎng)站性能來說,可能一兩個用戶的訪問對于減小服務(wù)器的負(fù)擔(dān)沒有明顯效果,但如果這個網(wǎng)站在高并發(fā)的情況下,使用緩存對于減小服務(wù)器壓力和整個網(wǎng)站的性能都會發(fā)生質(zhì)的變化。

緩存規(guī)則簡介

為了方便理解,我們認(rèn)為瀏覽器存在一個緩存數(shù)據(jù)庫,用于存儲緩存信息(實際上靜態(tài)資源是被緩存到了內(nèi)存和磁盤中),在瀏覽器第一次請求數(shù)據(jù)時,此時緩存數(shù)據(jù)庫沒有對應(yīng)的緩存數(shù)據(jù),則需要請求服務(wù)器,服務(wù)器會將緩存規(guī)則和數(shù)據(jù)返回,瀏覽器將緩存規(guī)則和數(shù)據(jù)存儲進(jìn)緩存數(shù)據(jù)庫。

當(dāng)瀏覽器地址欄輸入地址后請求的 index.html 是不會被緩存的,但 index.html 內(nèi)部請求的其他資源會遵循緩存策略,HTTP 緩存有多種規(guī)則,根據(jù)是否需要向服務(wù)器發(fā)送請求主要分為兩大類,強(qiáng)制緩存和協(xié)商緩存。

強(qiáng)制緩存

1、強(qiáng)制緩存流程

強(qiáng)制緩存是第一次訪問服務(wù)器獲取數(shù)據(jù)后,在有效時間內(nèi)不會再請求服務(wù)器,而是直接使用緩存數(shù)據(jù),強(qiáng)制緩存的流程如下。

2、強(qiáng)制緩存判斷到期時間

那么如何判斷緩存是否到期呢?其實還是根據(jù)第一次訪問時服務(wù)器的響應(yīng)頭來實現(xiàn)的,在 HTTP 1.0 版本和 HTTP 1.1 版本有所不同。

HTTP 1.0 版本,服務(wù)器使用的響應(yīng)頭字段為 Expires,值為未來的絕對時間(時間戳),瀏覽器請求時的當(dāng)前時間超過了 Expires 設(shè)置的時間,代表緩存失效,需要再次向服務(wù)器發(fā)送請求,否則都會直接從緩存數(shù)據(jù)庫中獲取數(shù)據(jù)。

HTTP 1.1 版本,服務(wù)器使用的響應(yīng)頭字段為 Cache-Control,有多個值,意義各不相同。

  • private:客戶端可以緩存;
  • public:客戶端和代理服務(wù)器都可以緩存(對于前端而言,可以認(rèn)為與 private 效果相同);
  • max-age=xxx:緩存的內(nèi)容將在 xxx 秒后過期(相對時間,秒為單位);
  • no-cache:需要使用協(xié)商緩存(后面介紹)來驗證數(shù)據(jù)是否過期;
  • no-store:所有內(nèi)容都不會緩存,強(qiáng)制緩存和協(xié)商緩存都不會觸發(fā)。

Cache-Control 的值中最常用的為 max-age=xxx,緩存本身就是為了數(shù)據(jù)傳輸?shù)膬?yōu)化和性能而存在的,所以 no-store 幾乎不會使用。

注意:在 HTTP 1.0 版本中,Expires 字段的絕對時間是從服務(wù)器獲取的,由于請求需要時間,所以瀏覽器的請求時間與服務(wù)器接收到請求所獲取的時間是存在誤差的,這也導(dǎo)致了緩存命中的誤差,在 HTTP 1.1 版本中,因為 Cache-Control 的值 max-age=xxx 中的 xxx 是以秒為單位的相對時間,所以在瀏覽器接收到資源后開始倒計時,規(guī)避了 HTTP 1.0 中緩存命中存在誤差的缺點,為了兼容低版本 HTTP 協(xié)議,正常開發(fā)中兩種響應(yīng)頭會同時使用,HTTP 1.1 版本的實現(xiàn)優(yōu)先級高于 HTTP 1.0。

3、通過 Network 查看強(qiáng)制緩存

我們通過 Chrome 瀏覽器的開發(fā)者工具,打開 NetWork 查看強(qiáng)制緩存的相關(guān)信息。

上面是百度網(wǎng)站 Logo 圖片的響應(yīng),我們可以清楚的看到,其中兼容了 HTTP 1.0HTTP 1.1 版本,并使用強(qiáng)制緩存存儲了 10 年。

下面看一看通過緩存取出的數(shù)據(jù)在 Network 中與其他資源的區(qū)別。

其實緩存的儲存是內(nèi)存和磁盤兩個位置,由當(dāng)前瀏覽器本身的策略決定,比較隨機(jī),從內(nèi)存的緩存中取出的數(shù)據(jù)會顯示 (from memory cache),從磁盤的緩存中取出的數(shù)據(jù)會顯示 (from disk cache)

4、NodeJS 服務(wù)器實現(xiàn)強(qiáng)制緩存

// 強(qiáng)制緩存
const http = require("http");
const url = require("url");
const path = require("path");
const mime = require("mime");
const fs = require("fs");

const server = http.createServer((req, res) => {
 let { pathname } = url.parse(req.url, true);
 pathname = pathname !== "/" ? pathname : "/index.html";

 // 獲取讀取文件的絕對路徑
 let p = path.join(__dirname, pathname);

 // 查看路徑是否合法
 fs.access(p, err => {
  // 路徑不合法則直接中斷連接
  if (err) return res.end("Not Found");

  // 設(shè)置強(qiáng)制緩存
  res.setHeader("Expires", new Date(Date.now() + 30000).toGMTString());
  res.setHeader("Cache-Control", "max-age=30");

  // 設(shè)置文件類型并響應(yīng)給瀏覽器
  res.setHeader("Content-Type", `${mime.getType(p)};charset=utf8`);
  fs.createReadStream(p).pipe(res);
 });
});

server.listen(3000, () => {
 console.log("server start 3000");
});

上面 mime 模塊的 getType 方法可以成功返回傳入路徑下文件對應(yīng)的文件類型,如 text/htmlapplication/javascript 等,是第三方模塊,使用之前需要安裝。

npm install mime

協(xié)商緩存

1、協(xié)商緩存流程

協(xié)商緩存又叫對比緩存,設(shè)置協(xié)商緩存后,第一次訪問服務(wù)器獲取數(shù)據(jù)時,服務(wù)器會將數(shù)據(jù)和緩存標(biāo)識一起返回給瀏覽器,客戶端會將數(shù)據(jù)和標(biāo)識存入緩存數(shù)據(jù)庫中,下一次請求時,會先去緩存中取出緩存標(biāo)識發(fā)送給服務(wù)器進(jìn)行詢問,當(dāng)服務(wù)器數(shù)據(jù)更改時會更新標(biāo)識,所以服務(wù)器拿到瀏覽器發(fā)來的標(biāo)識進(jìn)行對比,相同代表數(shù)據(jù)未更改,響應(yīng)瀏覽器通知數(shù)據(jù)未更改,瀏覽器會去緩存中獲取數(shù)據(jù),如果標(biāo)識不同,代表服務(wù)器更改過數(shù)據(jù),所以會將新的數(shù)據(jù)和新的標(biāo)識返回瀏覽器,瀏覽器會將新的數(shù)據(jù)和標(biāo)識存入緩存中,協(xié)商緩存的流程如下。

協(xié)商緩存和強(qiáng)制緩存不同的是,協(xié)商緩存每次請求都需要跟服務(wù)器通信,而且命中緩存服務(wù)器返回狀態(tài)碼不再是 200,而是 304。

2、協(xié)商緩存判斷標(biāo)識

強(qiáng)制緩存是通過過期時間來控制是否訪問服務(wù)器,而協(xié)商緩存每次都要與服務(wù)器交互對比緩存標(biāo)識,同樣的,對于協(xié)商緩存的實現(xiàn)在 HTTP 1.0 版本和 HTTP 1.1 版本也有所不同。

HTTP 1.0 版本中,服務(wù)器通過 Last-Modified 響應(yīng)頭來設(shè)置緩存標(biāo)識,通常取請求數(shù)據(jù)的最后修改時間(絕對時間)作為值,而瀏覽器將接收到返回的數(shù)據(jù)和標(biāo)識存入緩存,再次請求會自動發(fā)送 If-Modified-Since 請求頭,值為之前返回的最后修改時間(標(biāo)識),服務(wù)器取出 If-Modified-Since 的值與數(shù)據(jù)的上次修改時間對比,如果上次修改時間大于了 If-Modified-Since 的值,說明被修改過,則通過 Last-Modified 響應(yīng)頭返回新的最后修改時間和新的數(shù)據(jù),否則未被修改,返回狀態(tài)碼 304 通知瀏覽器命中緩存。

HTTP 1.1 版本中,服務(wù)器通過 Etag 響應(yīng)頭來設(shè)置緩存標(biāo)識(唯一標(biāo)識,像一個指紋一樣,生成規(guī)則由服務(wù)器來決定),瀏覽器接收到數(shù)據(jù)和唯一標(biāo)識后存入緩存,下次請求時,通過 If-None-Match 請求頭將唯一標(biāo)識帶給服務(wù)器,服務(wù)器取出唯一標(biāo)識與之前的標(biāo)識對比,不同,說明修改過,返回新標(biāo)識和數(shù)據(jù),相同,則返回狀態(tài)碼 304 通知瀏覽器命中緩存。

HTTP 協(xié)商緩存策略流程圖如下:

注意:使用協(xié)商緩存時 HTTP 1.0 版本還是不太靠譜,假設(shè)一個文件增加了一個字符后又刪除了,文件相當(dāng)于沒更改,但是最后修改時間變了,會被當(dāng)作修改處理,本應(yīng)該命中緩存,服務(wù)器卻重新發(fā)送了數(shù)據(jù),因此 HTTP 1.1 中使用的 Etag 唯一標(biāo)識是根據(jù)文件內(nèi)容或摘要生成的,保證了只要文件內(nèi)容不變,則一定會命中緩存,為了兼容低版本 HTTP 協(xié)議,開發(fā)中兩種響應(yīng)頭也會同時使用,同樣 HTTP 1.1 版本的實現(xiàn)優(yōu)先級高于 HTTP 1.0。

3、通過 Network 查看協(xié)商緩存

我們同樣通過 Chrome 瀏覽器的開發(fā)者工具,打開 NetWork 查看協(xié)商緩存的相關(guān)信息。

再次請求服務(wù)器的請求頭信息:

命中協(xié)商緩存的響應(yīng)頭信息:

下面看一看通過協(xié)商緩存取出的數(shù)據(jù)在 Network 中與第一次加載的區(qū)別。

第一次請求:

緩存后請求:

通過兩圖的對比,我們可以發(fā)現(xiàn),協(xié)商緩存生效時的狀態(tài)碼為 304,并且報文大小和請求時間大大減少,原因是服務(wù)端在進(jìn)行標(biāo)識比對后只返回了 header 部分,通過狀態(tài)碼來通知瀏覽器使用緩存,不再需要將報文主體部分一起返回給瀏覽器。

4、NodeJS 服務(wù)器實現(xiàn)協(xié)商緩存

// 協(xié)商緩存
const http = require("http");
const url = require("url");
const path = require("path");
const mime = require("mime");
const fs = require("fs");0
const crytpo = require("crytpo");

const server = http.createServer((req, res) => {
 let { pathname } = url.parse(req.url, true);
 pathname = pathname !== "/" ? pathname : "/index.html";

 // 獲取讀取文件的絕對路徑
 let p = path.join(__dirname, pathname);

 // 查看路徑是否合法
 fs.stat(p, (err, statObj) => {
  // 路徑不合法則直接中斷連接
  if (err) return res.end("Not Found");

  let md5 = crypto.createHash("md5"); // 創(chuàng)建加密的轉(zhuǎn)換流
  let rs = fs.createReadStream(p); // 創(chuàng)建可讀流

  // 讀取文件內(nèi)容并加密
  rs.on("data", data => md5.update(data));

  rs.on("end", () => {
   let ctime = statObj.ctime.toGMTString(); // 獲取文件最后修改時間
   let flag = md5.digest("hex"); // 獲取加密后的唯一標(biāo)識

   // 獲取協(xié)商緩存的請求頭
   let ifModifiedSince = req.headers["if-modified-since"];
   let ifNoneMatch = req.headers["if-none-match"];

   if (ifModifiedSince === ctime || ifNoneMatch === flag) {
    res.statusCode = 304;
    res.end();
   } else {
    // 設(shè)置協(xié)商緩存
    res.setHeader("Last-Modified", ctime);
    res.setHeader("Etag", flag);

    // 設(shè)置文件類型并響應(yīng)給瀏覽器
    res.setHeader("Content-Type", `${mime.getType(p)};charset=utf8`);
    rs.pipe(res);
   }
  });
 });
});

server.listen(3000, () => {
 console.log("server start 3000");
});

在上面的代碼中是通過可讀流讀取文件內(nèi)容,并通過 crypto 模塊進(jìn)行了 md5 加密后的結(jié)果作為了唯一標(biāo)識,這樣就能保證只要文件內(nèi)容不變,就會命中緩存,其中兼容了 HTTP 1.0HTTP 1.1 兩個版本,只要滿足一個則直接返回 304 通知瀏覽器命中緩存。

注意:其實讀取文件內(nèi)容加密這種做法并不可取,假如讀取的是大文件,在讀取文件內(nèi)容和進(jìn)行 md5 加密這個過程會非常消耗時間,所以在開發(fā)中要針對業(yè)務(wù)的實際情況選擇可以保證服務(wù)器性能的方式生成唯一標(biāo)識,比如根據(jù)文件的摘要。

總結(jié)

為了使緩存策略更加健壯、靈活,HTTP 1.0 版本 和 HTTP 1.1 版本的緩存策略會同時使用,甚至強(qiáng)制緩存和協(xié)商緩存也會同時使用,對于強(qiáng)制緩存,服務(wù)器通知瀏覽器一個緩存時間,在緩存時間內(nèi),下次請求,直接使用緩存,超出有效時間,執(zhí)行協(xié)商緩存策略,對于協(xié)商緩存,將緩存信息中的 EtagLast-Modified 通過請求頭 If-None-MatchIf-Modified-Since 發(fā)送給服務(wù)器,由服務(wù)器校驗同時設(shè)置新的強(qiáng)制緩存,校驗通過并返回 304 狀態(tài)碼時,瀏覽器直接使用緩存,如果協(xié)商緩存也未命中,則服務(wù)器重新設(shè)置協(xié)商緩存的標(biāo)識。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論