HTTP-緩存控制

靜態(tài)服務器(無緩存無更新請求數(shù)據(jù))

  • 優(yōu)點:簡單监透。
  • 缺點:每次請求必須查找返回原始文件舱禽,浪費帶寬琉历。

有緩存-無更新請求數(shù)據(jù)(瀏覽器本地緩存)

  • 優(yōu)點:節(jié)省資源社痛,速度快见转。
  • 缺點:服務器緩存中的數(shù)據(jù)變了,瀏覽器不知道數(shù)據(jù)是否發(fā)生改變蒜哀。

緩存作用:

緩存是指代理服務器或客戶端本地磁盤內(nèi)保存的資源副本斩箫,利用緩存可減少對源服務器的訪問,可以節(jié)省通信流量和通信時間撵儿。

有緩存有更新請求數(shù)據(jù)

  • 主要原理:請求被響應的時候乘客,響應報文中有一個Expires :Mon,10 Dec 1990 02:25:22GMT(過期時間),再一次進行請求的時候淀歇,用本地的時間與過期時間進行比較易核,如果本地時間小于過期時間,那么從緩存中獲取浪默,如果本地時間大于過期時間牡直,重新向服務器發(fā)送請求獲取,再一次發(fā)送新的過期時間纳决。
  • 優(yōu)點:緩存可控制碰逸。
  • 缺點:控制的功能太單一,這種格式的時間和容易寫錯阔加。

有緩存+更新機制升級版

Cache-Control: max-age=300饵史;

以上代碼代表時間間隔,如果再一次的請求在時間間隔300s之內(nèi)掸哑,就在緩存中獲取约急,否則從服務器獲取。

  • Cache-Control還有其他值:
    • Public表示響應可被任何中間節(jié)點緩存苗分,如 Browser <-- proxy1 <-- proxy2 <-- Server,中間的proxy可以緩存資源牵辣,比如下次再請求同一資源proxy1直接把自己緩存的東西給 Browser 而不再向proxy2要摔癣。
    • Private表示中間節(jié)點不允許緩存,對于Browser <-- proxy1 <-- proxy2 <-- Server纬向,proxy 會老老實實把Server 返回的數(shù)據(jù)發(fā)送給proxy1,自己不緩存任何數(shù)據(jù)择浊。當下次Browser再次請求時proxy會做好請求轉(zhuǎn)發(fā)而不是自作主張給自己緩存的數(shù)據(jù)。
    • no-cache表示不使用 Cache-Control的緩存控制方式做前置驗證逾条,而是使用 Etag 或者Last-Modified字段來控制緩存琢岩。不走緩存,響應報文师脂,是服務器發(fā)給瀏覽器的担孔。瀏覽器在一次發(fā)送請求時江锨,發(fā)現(xiàn)這個字段,就不會再緩存中獲取數(shù)據(jù)了糕篇,而是再一次向服務器發(fā)送請求啄育。 緩存只是本地緩存,而不是服務器對應的緩存拌消。報文會緩存挑豌,但是不會使用。
    • no-store 墩崩,真正的不緩存任何東西氓英。瀏覽器會直接向服務器請求原始文件,并且請求中不附帶 Etag 參數(shù)(服務器認為是新請求)鹦筹。不存铝阐,所有的流程都不進行緩存。連報文都不會進行緩存盛龄,啥都不緩存饰迹。
    • max-age,表示當前資源的有效時間余舶,單位為秒啊鸭。
  • 優(yōu)點: 緩存控制功能更強大
  • 缺點: 不夠完美,超過時間間隔再向服務器要文件的時候匿值,服務器會再一次發(fā)送源文件赠制,但如果文件未被改變,發(fā)送源文件太浪費帶寬了挟憔,只要發(fā)送一個文件未被更改的短信息標示就好了钟些。

緩存+更新終極版

服務器返回的文件以及額外信息,其中Etag 是 對請求文件的編碼绊谭,如果請求文件在服務端未被修改政恍,這個值就不會變。

Cache-Control: max-age=300达传;
ETag:W/"e-cbxLFQW5zapn79tQwb/g6Q"

當超過時間間隔的時候篙耗,重新發(fā)請求獲取源文件的時候,在發(fā)送請求的時候附帶剛剛保存的文件的ETag ( If-None-Match:W/"e-cbxLFQW5zapn79tQwb/g6Q")宪赶,之后于ETag進行比較宗弯,如果二者相等,則發(fā)送個短消息(響應頭搂妻,不包含圖片內(nèi)容, 304)蒙保,如果二者不等則發(fā)送新文件和新的 ETag,瀏覽器獲取新文件并更新該文件的 Etag欲主。(瀏覽器的默認行為邓厕。)

與 ETag 類似功能的是Last-Modified/If-Modified-Since逝嚎。當資源過期時(max-age超時),發(fā)現(xiàn)資源具有Last-Modified聲明邑狸,則再次向web服務器請求時帶上頭 If-Modified-Since懈糯,表示上次服務器告知的文件修改的時間。web服務器收到請求后發(fā)現(xiàn)有頭If-Modified-Since 則與被請求資源的最后修改時間進行比對单雾。若最后修改時間較新赚哗,說明資源又被改動過,則響應整片資源內(nèi)容(200)硅堆;若最后修改時間較舊屿储,說明資源無新修改,則響應HTTP 304 渐逃,告知瀏覽器繼續(xù)使用所保存的cache够掠。第一次去請求,響應頭中存在Last-modified,刷新后第二次請求茄菊,請求頭中有if-modified-since疯潭。

.html不會緩存,.css和圖片都會

原因:圖片和CSS的請求都是HTML到達瀏覽器后,瀏覽器解析發(fā)出的面殖,而HTML是直接輸入URL解析出來的竖哩。報文也存在差異:


htmlq請求報文.PNG

css報文.PNG
  • .html文件的報文中瀏覽器自動添加'Cache-Control:max-age=0',也表示.html文件不能使用瀏覽器內(nèi)部緩存。
  • 當瀏覽器檢測到狀態(tài)碼是304的時候脊僚,就會在本地的緩存中拿數(shù)據(jù)相叁,做一個展示。調(diào)試的時候以為是服務器發(fā)過來的辽幌,實際上并不是增淹。
  • 勾選開發(fā)者工具控制臺Disable cache原理:瀏覽器自動在請求報文上添加:pragma:no-cache。

Expires和Pragma字段對緩存的控制(http1.0版本)

  • Expires
//報文的響應頭
res.setHeader('Expires','web, 23 Jan 2019 07:40:51 GMT')

瀏覽器收到響應報文乌企,看到這個字段就會進行處理虑润,把它和當前瀏覽器的時間進行比較,如果滅有超過他加酵,則使用瀏覽器自己緩存的數(shù)據(jù)端辱,如果超過了,則重新獲取虽画。
更好的代碼寫法

let data = new Date(Date.now() + 1000*5).toGMTString()
res.setHeader('Expires',data)
  • Pragma
//響應頭
res.setHeader('Pragma','no-cache')

不要這個緩存。告訴瀏覽器不要用本地的緩存荣病,要向服務器重新獲取數(shù)據(jù)码撰。

  • 同時存在
    如果這兩個字段同時存在,則Pragma的優(yōu)先級是比較高的个盆。

-Cache-Control-對字段對緩存的控制

res.setHeader('Cache-Control','max-age=10')

表示數(shù)據(jù)可以使用10秒脖岛,在10秒內(nèi)不需要重新獲取數(shù)據(jù)朵栖。可以直接使用瀏覽器內(nèi)部緩存的數(shù)據(jù)柴梆。

res.setHeader('Cache-Control','no-cache')

不走緩存陨溅,是在響應報文上,是服務器發(fā)給瀏覽器的绍在。下一次瀏覽器發(fā)送請求的時候门扇,都會重新向服務器要數(shù)據(jù)。

res.setHeader('Cache-Control','no-store')

不存偿渡。所有的地方都不進行緩存臼寄。

-Last-Modified字段對緩存的控制

Cache-Control: no-cache
LastModified:sXXX

可以進行緩存,但是下一次請求之前溜宽,不可以直接在緩存中拿數(shù)據(jù)吉拳,要先問服務器是否可以在本地緩存中中拿,如果服務器返回304狀態(tài)碼适揉,則表示可以在本地緩存中拿數(shù)據(jù)留攒,否則,服務器返回數(shù)據(jù)嫉嘀。

const http = require('http')
const fs = require('fs')
const path = require('path')
http.createServer((req, res) => {
    let filePath = path.join(__dirname, req.url)
  fs.readFile(filePath, (err, data) => {
    if (err) {
        res.writeHead(404, 'not found')
        res.end('Oh, Not Found')
    } else {
let mtime = Date.parse(fs.statSync(filePath).mtime)
      //10秒內(nèi)炼邀,瀏覽器直接從自己本地拿,10秒后找服務器要吃沪。如果沒修改汤善,告訴瀏覽器沒修改就行,如果修改了票彪,給瀏覽器最新的
      res.setHeader('Cache-Control', 'max-age=10')

      if(!req.headers['if-modified-since']) {
        res.setHeader('Last-Modified', new Date(mtime).toGMTString())
        res.writeHead(200, 'OK')
        res.end(data)        
      }else {
        let oldMtime = Date.parse(req.headers['if-modified-since'])
        if(mtime > oldMtime) {
          res.setHeader('Last-Modified', new Date(mtime).toGMTString())
          res.writeHead(200, 'OK')
          res.end(data)            
        }else {
          res.writeHead(304)
          res.end()
        }
      }

    }
  })
}).listen(8080)
console.log('Visit http://localhost:8080' )

-Etag字段對緩存的控制

Etag就是對文件進行一個處理红淡,值的獲取是由自己決定的。需要的文件和這個值進行一個映射降铸,值變了在旱,文件就變了。

   res.setHeader('Etag', md5.update(data).digest('base64'))
//data進行一個計算推掸,得到md5值桶蝎,當文件內(nèi)容發(fā)生改變的時候,md5值肯定會變谅畅。把md5的值用base64展示登渣,值會很短。

當攜帶Etag字段的報文到達瀏覽器之后毡泻,當瀏覽器下一次再去請求數(shù)據(jù)的時候胜茧,請求頭中就會有一個If-None-Match這個值,會把這個值在發(fā)給服務器。會先看一下當前文件的新的值呻顽,進行對比雹顺。實質(zhì)就是檢測當前文件的完整性。


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

http.createServer((req, res) => {
  let filePath = path.join(__dirname, req.url)
  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(404, 'not found')
      res.end('Oh, Not Found')
    } else {
      // example1
      // let md5 = crypto.createHash('md5')
      // res.setHeader('Etag', md5.update(data).digest('base64'))
      // res.writeHead(200, 'OK')
      // res.end(data)

      // example2
      console.log(req.headers['if-none-match'])
      let oldEtag = req.headers['if-none-match']
      if(!oldEtag) {
        let md5 = crypto.createHash('md5')
        res.setHeader('Etag', md5.update(data).digest('base64'))
        res.writeHead(200, 'OK')
        res.end(data)       
      } else {
        let newEtag = crypto.createHash('md5').update(data).digest('base64')
        if(oldEtag !== newEtag) {
          res.setHeader('Etag', newEtag)
          res.writeHead(200, 'OK')
          res.end(data)          
        }else {
          res.writeHead(304)
          res.end()
        }
      }
}
  })
}).listen(8080)
console.log('Visit http://localhost:8080' )

小結(jié):

  1. 在互聯(lián)網(wǎng)上廊遍,域名通過DNS服務映射到IP地址之后訪問目標網(wǎng)站嬉愧,也就是說,當請求到達服務器時喉前,已經(jīng)是已IP地址形式訪問了没酣。
  2. 代理:是一種具有轉(zhuǎn)發(fā)功能的應用程序,它扮演了位于服務器和客戶端“中間人”的角色被饿,接收由客戶端發(fā)送的請求并轉(zhuǎn)發(fā)給服務器四康,同時也接收服務器返回的響應并轉(zhuǎn)發(fā)給客戶端。代理不會改變請求URI狭握。每次通過代理服務器轉(zhuǎn)發(fā)請求或者響應的時候闪金,會追加寫入Via首部信息。GET/HTTP/1.1 Via:proxy1论颅。
    • 緩存代理(利用緩存技術(shù))哎垦;
    • 透明代理,不對報文做任何加工的代理叫透明代理恃疯。
  3. 網(wǎng)關(guān):是轉(zhuǎn)發(fā)其他服務器通信數(shù)據(jù)的服務器漏设,接收從客戶端發(fā)來的請求時,它就像自己擁有資源的服務器一樣今妄,對請求進行處理郑口。利用網(wǎng)關(guān)可以由HTTP請求轉(zhuǎn)化為其他協(xié)議通信。
  4. 隧道:是在相隔甚遠的客戶端和服務器兩者之間進行中轉(zhuǎn)盾鳞,并保持雙方通信連接的應用程序犬性。隧道的目的是確保客戶端能與服務器進行安全的通信腾仅,本身 不會去解析HTTP請求乒裆,請求保持原樣中轉(zhuǎn)給之后的服務器。隧道會在通信雙方斷開連接時結(jié)束推励。
  5. 需要兼容HTTP1.0的時候需要使用Expires鹤耍,不然可以考慮直接使用Cache-Control
  6. 需要處理一秒內(nèi)多次修改的情況,或者其他Last-Modified處理不了的情況验辞,才使用ETag稿黄,否則使用Last-Modified。
  7. 對于所有可緩存資源跌造,需要指定一個Expires或Cache-Control抛猖,同時指定Last-Modified或者Etag。
  8. 可以通過標識文件版本名、加長緩存時間的方式來減少304響應财著。

學習資料參考:HTTP緩存控制小結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撑碴,隨后出現(xiàn)的幾起案子撑教,更是在濱河造成了極大的恐慌,老刑警劉巖醉拓,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伟姐,死亡現(xiàn)場離奇詭異,居然都是意外死亡亿卤,警方通過查閱死者的電腦和手機愤兵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來排吴,“玉大人秆乳,你說我怎么就攤上這事∽炅ǎ” “怎么了屹堰?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長街氢。 經(jīng)常有香客問我扯键,道長,這世上最難降的妖魔是什么珊肃? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任荣刑,我火速辦了婚禮,結(jié)果婚禮上伦乔,老公的妹妹穿的比我還像新娘厉亏。我一直安慰自己,他們只是感情好评矩,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布叶堆。 她就那樣靜靜地躺著,像睡著了一般斥杜。 火紅的嫁衣襯著肌膚如雪虱颗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天蔗喂,我揣著相機與錄音忘渔,去河邊找鬼。 笑死缰儿,一個胖子當著我的面吹牛畦粮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼宣赔,長吁一口氣:“原來是場噩夢啊……” “哼预麸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起儒将,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤吏祸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钩蚊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡翘,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年砰逻,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸣驱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝠咆,死狀恐怖踊东,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情勺美,我是刑警寧澤递胧,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站赡茸,受9級特大地震影響缎脾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜占卧,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一遗菠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧华蜒,春花似錦辙纬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捂蕴,卻和暖如春譬涡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啥辨。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工涡匀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溉知。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓陨瘩,卻偏偏與公主長得像腕够,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舌劳,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361