HTTP 緩存

強緩存

HTTP/1.0 使用的是 Expires 字段至朗,HTTP/1.1 使用的是 Cache-Control 字段词渤。

  • Expires 即過期時間嗓违,時間是相對于服務器的時間而言的干像,存在于服務端返回的響應頭中蛙埂,在這個過期時間之前可以直接從緩存里面獲取數(shù)據(jù)倦畅,無需再次請求。但這種方式存在一個問題:無需再次請求服務器的時間和瀏覽器的時間可能并不一致绣的。
  • 在 HTTP 1.1 中叠赐,使用的是 Cache-Control 字段,這個字段采用的時間是過期時長屡江,對應的字段是 max-age=*芭概。

以下是使用 Node 模擬的簡單示例

const http = require('http')
const fs = require('fs')

const server = http.createServer((req, res) => {
  const time = new Date()
  time.setTime(time.getTime() + 100 * 1000) // 時間戳計算后 10s
  const expires = time.toUTCString() // GMT(格林尼治標準時間)時間格式
  res.setHeader('Expires', expires)

  // 優(yōu)先級高于 Expires
  res.setHeader('Cache-Control', 'max-age=30')
  const html = fs.readFileSync('./src/index.html', 'utf8')
  res.end(html)
})

server.listen(3000)

ExpiresCache-Control 同時存在時,優(yōu)先考慮 Cache-Control 字段惩嘉。當緩存資源失效了罢洲,也就是沒有命中強緩存,接下來就進入?yún)f(xié)商緩存文黎。

協(xié)商緩存

如果緩存過期了惹苗,我們就可以使用協(xié)商緩存來解決問題。協(xié)商緩存需要請求耸峭,如果緩存有效會返回 304桩蓉。協(xié)商緩存需要客戶端和服務端共同實現(xiàn),和強緩存?樣劳闹,也有兩種實現(xiàn)方式 Last-ModifiedETag

Last-Modified

Last-Modified 表示本地文件最后修改時間院究,If-Modified-Since 會將 Last-Modified 的值發(fā)送給服務器洽瞬,詢問服務器在該時間后資源是否有更新,有更新的話就會將新的資源發(fā)送回來业汰。

以下是使用 Node 模擬的簡單示例

const http = require('http')
const fs = require('fs')

const server = http.createServer((req, res) => {
  const reqModified = req.headers['if-modified-since']
  const info = fs.statSync('./src/index.html')
  const lastModified = info.mtime.toUTCString() // GTM

  if (reqModified && reqModified === lastModified) {
    console.log('瀏覽器走緩存')
    res.statusCode = 304
    res.end()
    return
  }

  res.setHeader('Last-Modified', lastModified)
  const html = fs.readFileSync('./src/index.html', 'utf8')
  res.end(html)
})

server.listen(3000)

但是如果在本地打開緩存文件伙窃,就會造成 Last-Modified 被修改,所以在 HTTP/1.1 出現(xiàn)了 ETag蔬胯。

ETag

ETag 類似于文件指紋对供,If-None-Match 會將當前 ETag 發(fā)送給服務器,詢問該資源 ETag 是否變動氛濒,有變動的話就將新的資源發(fā)送回來产场。并且 ETag 優(yōu)先級? Last-Modified 高。

const http = require('http')
const fs = require('fs')
const crypto = require('crypto')

const server = http.createServer((req, res) => {
  const buffer = fs.readFileSync('./src/index.html') // 二進制文件流
  const hashTool = crypto.createHash('md5') // 使用 md5 加密算法
  hashTool.update(buffer, 'utf8') // 注入二進制
  const md5 = hashTool.digest('hex') // 生成 md5 加密的唯一標識 hash
  const reqETag = req.headers['if-none-match']
  if (reqETag && reqETag === md5) {
    console.log('ETag 緩存')
    res.statusCode = 304
    res.end()
    return
  }

  const reqModified = req.headers['if-modified-since']
  const info = fs.statSync('./src/index.html')
  const lastModified = info.mtime.toUTCString() // GTM

  if (reqModified && reqModified === lastModified) {
    console.log('modified 緩存')
    res.statusCode = 304
    res.end()
    return
  }

  res.setHeader('Last-Modified', lastModified)
  res.setHeader('ETag', md5)
  const html = fs.readFileSync('./src/index.html', 'utf8')
  res.end(html)
})

server.listen(3000)

注意:以上使用 Node 進行模擬的示例舞竿,真實情況具體如何要看服務器那邊如何配置京景。

Last-ModifiedETag 的對比

  • 性能上,Last-Modified 優(yōu)于 ETag骗奖,Last-Modified 記錄的是時間點确徙,而 Etag 需要根據(jù)文件的 MD5 算法生成對應的 hash 值。
  • 精度上执桌,ETag 優(yōu)于 Last-Modified鄙皇。ETag 按照內(nèi)容給資源帶上標識,能準確感知資源變化仰挣,Last-Modified 在某些場景并不能準確感知變化伴逸。

如果兩者都存在,優(yōu)先判斷 If-None-Match 進行 ETag 協(xié)商緩存膘壶。

緩存位置

當命中強緩存和協(xié)商緩存返回 304 時错蝴,瀏覽器會從緩存中獲取資源。

瀏覽器中的緩存位置一共有四種颓芭,按優(yōu)先級從高到低排列分別是:

  • Service Worker — 其借鑒了 Web Worker 思路顷锰,主要功能有:離線緩存、消息推送和網(wǎng)絡代理亡问,其中離線緩存就是 Service Worker Cache官紫。
  • Memory Cache — 內(nèi)存緩存,從效率上講它是最快的州藕,從存活時間來講又是最短的万矾,當渲染進程結(jié)束后,內(nèi)存緩存也就不存在了慎框。
  • Disk Cache — 存儲在磁盤中的緩存良狈,從存取效率上講是比內(nèi)存緩存慢的,優(yōu)勢在于存儲容量和存儲時長笨枯。
  • Push Cache — 推送緩存薪丁,它瀏覽器緩存的最后一道防線遇西,它是 HTTP/2 的內(nèi)容,詳細內(nèi)容可以查看 HTTP/2 push is tougher than I thought

瀏覽器在選擇 Disk Cache 與 Memory Cache 的存儲上:內(nèi)容使用率高的严嗜,文件優(yōu)先進入磁盤粱檀。比較大的 JS、CSS 文件會直接放入磁盤漫玄,反之放入內(nèi)存茄蚯。

強緩存與協(xié)商緩存區(qū)別

  • 強緩存 — 瀏覽器不會與服務端協(xié)商,而是直接獲取瀏覽器緩存睦优。
  • 協(xié)商緩存 — 瀏覽器會先向服務器確認資源的有效性后渗常,才決定是從緩存中獲取資源還是重新獲取資源。
  • 強緩存在瀏覽器進行判斷汗盘,而協(xié)商緩存在服務端進行判斷

總結(jié)

首先檢查 Cache-Control皱碘,驗證強緩存是否可用

  • 如果可用的話,直接使用
  • 否則進入?yún)f(xié)商緩存隐孽,發(fā)送 HTTP 請求癌椿,服務器通過請求頭中的 If-Modified-Since 或者 If-None-Match 字段檢查資源是否更新
    • 資源更新,返回資源和 200 狀態(tài)碼菱阵。
    • 否則踢俄,返回 304,告訴瀏覽器直接從緩存中獲取資源晴及。

本文首發(fā) blog褪贵,如果喜歡或者有所啟發(fā),歡迎 Star抗俄,對作者也是一種鼓勵。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末世舰,一起剝皮案震驚了整個濱河市动雹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跟压,老刑警劉巖胰蝠,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異震蒋,居然都是意外死亡茸塞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門查剖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钾虐,“玉大人,你說我怎么就攤上這事笋庄⌒ǎ” “怎么了倔监?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長菌仁。 經(jīng)常有香客問我浩习,道長,這世上最難降的妖魔是什么济丘? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任谱秽,我火速辦了婚禮,結(jié)果婚禮上摹迷,老公的妹妹穿的比我還像新娘疟赊。我一直安慰自己,他們只是感情好泪掀,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布听绳。 她就那樣靜靜地躺著,像睡著了一般异赫。 火紅的嫁衣襯著肌膚如雪椅挣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天塔拳,我揣著相機與錄音鼠证,去河邊找鬼。 笑死靠抑,一個胖子當著我的面吹牛量九,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颂碧,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼荠列,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了载城?” 一聲冷哼從身側(cè)響起肌似,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诉瓦,沒想到半個月后川队,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡睬澡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年固额,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煞聪。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡斗躏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昔脯,到底是詐尸還是另有隱情瑟捣,我是刑警寧澤馋艺,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站迈套,受9級特大地震影響捐祠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜桑李,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一踱蛀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贵白,春花似錦率拒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至呛伴,卻和暖如春勃痴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背热康。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工沛申, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人姐军。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓铁材,卻偏偏與公主長得像,于是被迫代替她去往敵國和親奕锌。 傳聞我的和親對象是個殘疾皇子著觉,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容