摘要:請(qǐng)求過(guò)程如下瀏覽器請(qǐng)求靜態(tài)資源服務(wù)器讀取磁盤(pán)文件,返給瀏覽器,同時(shí)帶上文件的唯一標(biāo)識(shí)當(dāng)瀏覽器上的緩存文件過(guò)期時(shí),瀏覽器帶上請(qǐng)求頭等于上一次請(qǐng)求的請(qǐng)求服務(wù)器服務(wù)器比較請(qǐng)求頭里的和文件的。
前言 Http簡(jiǎn)介
瀏覽器和服務(wù)器之間通信是通過(guò)HTTP協(xié)議,HTTP協(xié)議永遠(yuǎn)都是客戶端發(fā)起請(qǐng)求,服務(wù)器回送響應(yīng)。模型如下:
HTTP報(bào)文就是瀏覽器和服務(wù)器間通信時(shí)發(fā)送及響應(yīng)的數(shù)據(jù)塊。瀏覽器向服務(wù)器請(qǐng)求數(shù)據(jù),發(fā)送請(qǐng)求(request)報(bào)文;服務(wù)器向?yàn)g覽器返回?cái)?shù)據(jù),返回響應(yīng)(response)報(bào)文。報(bào)文信息主要分為兩部分:
報(bào)文頭部:一些附加信息(cookie,緩存信息等),與緩存相關(guān)的規(guī)則信息,均包含在頭部中
數(shù)據(jù)主體部分:HTTP請(qǐng)求真正想要傳輸?shù)臄?shù)據(jù)內(nèi)容
我們?yōu)槭裁词褂镁彺妫且驗(yàn)榫彺婵梢越o我們的 Web 項(xiàng)目帶來(lái)以下好處,以提高性能和用戶體驗(yàn)。
加快了瀏覽器加載網(wǎng)頁(yè)的速度;
減少了冗余的數(shù)據(jù)傳輸,節(jié)省網(wǎng)絡(luò)流量和帶寬;
減少服務(wù)器的負(fù)擔(dān),大大提高了網(wǎng)站的性能。
由于從本地緩存讀取靜態(tài)資源,加快瀏覽器的網(wǎng)頁(yè)加載速度是一定的,也確實(shí)的減少了數(shù)據(jù)傳輸,就提高網(wǎng)站性能來(lái)說(shuō),可能一兩個(gè)用戶的訪問(wèn)對(duì)于減小服務(wù)器的負(fù)擔(dān)沒(méi)有明顯效果,但如果這個(gè)網(wǎng)站在高并發(fā)的情況下,使用緩存對(duì)于減小服務(wù)器壓力和整個(gè)網(wǎng)站的性能都會(huì)發(fā)生質(zhì)的變化。
原始模型(不是用緩存)搭建一個(gè)Express的服務(wù)器,不加任何緩存信息頭:
const express = require("express"); const app = express(); const port = 8080; const fs = require("fs"); const path = require("path"); app.get("/",(req,res) => { res.send(`Document Http Cache Demo `) }) app.get("/demo.js",(req, res)=>{ let jsPath = path.resolve(__dirname,"./static/js/demo.js"); let cont = fs.readFileSync(jsPath); res.end(cont) }) app.listen(port,()=>{ console.log(`listen on ${port}`) })
我們可以看到請(qǐng)求結(jié)果如下:
請(qǐng)求過(guò)程如下:
瀏覽器請(qǐng)求靜態(tài)資源demo.js
服務(wù)器讀取磁盤(pán)文件demo.js,返給瀏覽器
瀏覽器再次請(qǐng)求,服務(wù)器又重新讀取磁盤(pán)文件 demo.js,返給瀏覽器。
循環(huán)請(qǐng)求。。
??看得出來(lái)這種請(qǐng)求方式的流量與請(qǐng)求次數(shù)有關(guān),同時(shí),缺點(diǎn)也很明顯:
浪費(fèi)用戶流量
浪費(fèi)服務(wù)器資源,服務(wù)器要讀磁盤(pán)文件,然后發(fā)送文件到瀏覽器
瀏覽器要等待js下載并且執(zhí)行后才能渲染頁(yè)面,影響用戶體驗(yàn)
緩存規(guī)則為了方便理解,我們認(rèn)為瀏覽器存在一個(gè)緩存數(shù)據(jù)庫(kù),用于存儲(chǔ)緩存信息(實(shí)際上靜態(tài)資源是被緩存到了內(nèi)存和磁盤(pán)中),在瀏覽器第一次請(qǐng)求數(shù)據(jù)時(shí),此時(shí)緩存數(shù)據(jù)庫(kù)沒(méi)有對(duì)應(yīng)的緩存數(shù)據(jù),則需要請(qǐng)求服務(wù)器,服務(wù)器會(huì)將緩存規(guī)則和數(shù)據(jù)返回,瀏覽器將緩存規(guī)則和數(shù)據(jù)存儲(chǔ)進(jìn)緩存數(shù)據(jù)庫(kù)。
當(dāng)瀏覽器地址欄輸入地址后請(qǐng)求的 index.html 是不會(huì)被緩存的,但 index.html 內(nèi)部請(qǐng)求的其他資源會(huì)遵循緩存策略,HTTP 緩存有多種規(guī)則,根據(jù)是否需要向服務(wù)器發(fā)送請(qǐng)求主要分為兩大類,強(qiáng)制緩存和協(xié)商緩存。
Http緩存的分類Http緩存可以分為兩大類,強(qiáng)制緩存(也稱強(qiáng)緩存)和協(xié)商緩存。兩類緩存規(guī)則不同,強(qiáng)制緩存在緩存數(shù)據(jù)未失效的情況下,不需要再和服務(wù)器發(fā)生交互;而協(xié)商緩存,顧名思義,需要進(jìn)行比較判斷是否可以使用緩存。
??兩類緩存規(guī)則可以同時(shí)存在,強(qiáng)制緩存優(yōu)先級(jí)高于協(xié)商緩存,也就是說(shuō),當(dāng)執(zhí)行強(qiáng)制緩存的規(guī)則時(shí),如果緩存生效,直接使用緩存,不再執(zhí)行協(xié)商緩存規(guī)則。
強(qiáng)制緩存是第一次訪問(wèn)服務(wù)器獲取數(shù)據(jù)后,在有效時(shí)間內(nèi)不會(huì)再請(qǐng)求服務(wù)器,而是直接使用緩存數(shù)據(jù),強(qiáng)制緩存的流程如下:
強(qiáng)制緩存分為兩種情況,Expires和Cache-Control。
ExpiresExpires的值是服務(wù)器告訴瀏覽器的緩存過(guò)期時(shí)間(值為GMT時(shí)間,即格林尼治時(shí)間),即下一次請(qǐng)求時(shí),如果瀏覽器端的當(dāng)前時(shí)間還沒(méi)有到達(dá)過(guò)期時(shí)間,則直接使用緩存數(shù)據(jù)。下面通過(guò)我們的Express服務(wù)器來(lái)設(shè)置一下Expires響應(yīng)頭信息。
//其他代碼... const moment = require("moment"); app.get("/demo.js",(req, res)=>{ let jsPath = path.resolve(__dirname,"./static/js/demo.js"); let cont = fs.readFileSync(jsPath); res.setHeader("Expires", getGLNZ()) //2分鐘 res.end(cont) }) function getGLNZ(){ return moment().utc().add(2,"m").format("ffffd, DD MMM YYYY HH:mm:ss")+" GMT"; } //其他代碼...
我們?cè)赿emo.js中添加了一個(gè)Expires響應(yīng)頭,不過(guò)由于是格林尼治時(shí)間,所以通過(guò)momentjs轉(zhuǎn)換一下。第一次請(qǐng)求的時(shí)候還是會(huì)向服務(wù)器發(fā)起請(qǐng)求,同時(shí)會(huì)把過(guò)期時(shí)間和文件一起返回給我們;但是當(dāng)我們刷新的時(shí)候,才是見(jiàn)證奇跡的時(shí)刻:
可以看出文件是直接從緩存(memory cache)中讀取的,并沒(méi)有發(fā)起請(qǐng)求。我們?cè)谶@邊設(shè)置過(guò)期時(shí)間為兩分鐘,兩分鐘過(guò)后可以刷新一下頁(yè)面看到瀏覽器再次發(fā)送請(qǐng)求了。
??雖然這種方式添加了緩存控制,節(jié)省流量,但是還是有以下幾個(gè)問(wèn)題的:
由于瀏覽器時(shí)間和服務(wù)器時(shí)間不同步,如果瀏覽器設(shè)置了一個(gè)很后的時(shí)間,過(guò)期時(shí)間一直沒(méi)有用
緩存過(guò)期后,不管文件有沒(méi)有發(fā)生變化,服務(wù)器都會(huì)再次讀取文件返回給瀏覽器
不過(guò)Expires 是HTTP 1.0的東西,現(xiàn)在默認(rèn)瀏覽器均默認(rèn)使用HTTP 1.1,所以它的作用基本忽略。
Cache-Control針對(duì)瀏覽器和服務(wù)器時(shí)間不同步,加入了新的緩存方案;這次服務(wù)器不是直接告訴瀏覽器過(guò)期時(shí)間,而是告訴一個(gè)相對(duì)時(shí)間Cache-Control=10秒,意思是10秒內(nèi),直接使用瀏覽器緩存。
Cache-Control各個(gè)值的含義:
**private**:客戶端可以緩存; **public**:客戶端和代理服務(wù)器都可以緩存(對(duì)于前端而言,可以認(rèn)為與 private 效果相同); **max-age=xxx**:緩存的內(nèi)容將在 xxx 秒后過(guò)期(相對(duì)時(shí)間,秒為單位); **no-cache**:需要使用協(xié)商緩存(后面介紹)來(lái)驗(yàn)證數(shù)據(jù)是否過(guò)期; **no-store**:所有內(nèi)容都不會(huì)緩存,強(qiáng)制緩存和協(xié)商緩存都不會(huì)觸發(fā)。
app.get("/demo.js",(req, res)=>{ let jsPath = path.resolve(__dirname,"./static/js/demo.js"); let cont = fs.readFileSync(jsPath); res.setHeader("Cache-Control", "public,max-age=120") //2分鐘 res.end(cont) })
其實(shí)緩存的儲(chǔ)存是內(nèi)存和磁盤(pán)兩個(gè)位置,由當(dāng)前瀏覽器本身的策略決定,比較隨機(jī),從內(nèi)存的緩存中取出的數(shù)據(jù)會(huì)顯示 (from memory cache),從磁盤(pán)的緩存中取出的數(shù)據(jù)會(huì)顯示 (from disk cache)。
協(xié)商緩存強(qiáng)制緩存的弊端很明顯,即每次都是根據(jù)時(shí)間來(lái)判斷緩存是否過(guò)期;但是當(dāng)?shù)竭_(dá)過(guò)期時(shí)間后,如果文件沒(méi)有改動(dòng),再次去獲取文件就有點(diǎn)浪費(fèi)服務(wù)器的資源了。
協(xié)商緩存又叫對(duì)比緩存,設(shè)置協(xié)商緩存后,第一次訪問(wèn)服務(wù)器獲取數(shù)據(jù)時(shí),服務(wù)器會(huì)將數(shù)據(jù)和緩存標(biāo)識(shí)一起返回給瀏覽器,客戶端會(huì)將數(shù)據(jù)和標(biāo)識(shí)存入緩存數(shù)據(jù)庫(kù)中,下一次請(qǐng)求時(shí),會(huì)先去緩存中取出緩存標(biāo)識(shí)發(fā)送給服務(wù)器進(jìn)行詢問(wèn),當(dāng)服務(wù)器數(shù)據(jù)更改時(shí)會(huì)更新標(biāo)識(shí),所以服務(wù)器拿到瀏覽器發(fā)來(lái)的標(biāo)識(shí)進(jìn)行對(duì)比,相同代表數(shù)據(jù)未更改,響應(yīng)瀏覽器通知數(shù)據(jù)未更改,瀏覽器會(huì)去緩存中獲取數(shù)據(jù),如果標(biāo)識(shí)不同,代表服務(wù)器更改過(guò)數(shù)據(jù),所以會(huì)將新的數(shù)據(jù)和新的標(biāo)識(shí)返回瀏覽器,瀏覽器會(huì)將新的數(shù)據(jù)和標(biāo)識(shí)存入緩存中,協(xié)商緩存的流程如下:
協(xié)商緩存和強(qiáng)制緩存不同的是,協(xié)商緩存每次請(qǐng)求都需要跟服務(wù)器通信,而且命中緩存服務(wù)器返回狀態(tài)碼不再是 200,而是 304。
協(xié)商緩存有兩組報(bào)文結(jié)合使用:
Last-Modified和If-Modified-Since
ETag和If-None-Match
Last-ModifiedHTTP 1.0 版本中:
為了節(jié)省服務(wù)器的資源,再次改進(jìn)方案。瀏覽器和服務(wù)器協(xié)商,服務(wù)器每次返回文件的同時(shí),告訴瀏覽器文件在服務(wù)器上最近的修改時(shí)間。請(qǐng)求過(guò)程如下:
瀏覽器請(qǐng)求靜態(tài)資源demo.js
服務(wù)器讀取磁盤(pán)文件demo.js,返給瀏覽器,同時(shí)帶上文件上次修改時(shí)間 Last-Modified(GMT標(biāo)準(zhǔn)格式)
當(dāng)瀏覽器上的緩存文件過(guò)期時(shí),瀏覽器帶上請(qǐng)求頭If-Modified-Since(等于上一次請(qǐng)求的Last-Modified)請(qǐng)求服務(wù)器
服務(wù)器比較請(qǐng)求頭里的If-Modified-Since和文件的上次修改時(shí)間。如果果一致就繼續(xù)使用本地緩存(304),如果不一致就再次返回文件內(nèi)容和Last-Modified。
循環(huán)請(qǐng)求。。
代碼實(shí)現(xiàn)過(guò)程如下:
app.get("/demo.js",(req, res)=>{ let jsPath = path.resolve(__dirname,"./static/js/demo.js") let cont = fs.readFileSync(jsPath); let status = fs.statSync(jsPath) let lastModified = status.mtime.toUTCString() if(lastModified === req.headers["if-modified-since"]){ res.writeHead(304, "Not Modified") res.end() } else { res.setHeader("Cache-Control", "public,max-age=5") res.setHeader("Last-Modified", lastModified) res.writeHead(200, "OK") res.end(cont) } })
雖然這個(gè)方案比前面三個(gè)方案有了進(jìn)一步的優(yōu)化,瀏覽器檢測(cè)文件是否有修改,如果沒(méi)有變化就不再發(fā)送文件;但是還是有以下缺點(diǎn):
由于Last-Modified修改時(shí)間是GMT時(shí)間,只能精確到秒,如果文件在1秒內(nèi)有多次改動(dòng),服務(wù)器并不知道文件有改動(dòng),瀏覽器拿不到最新的文件
如果服務(wù)器上文件被多次修改了但是內(nèi)容卻沒(méi)有發(fā)生改變,服務(wù)器需要再次重新返回文件。
ETagHTTP 1.1 版本中:
為了解決文件修改時(shí)間不精確帶來(lái)的問(wèn)題,服務(wù)器和瀏覽器再次協(xié)商,這次不返回時(shí)間,返回文件的唯一標(biāo)識(shí)ETag。只有當(dāng)文件內(nèi)容改變時(shí),ETag才改變。請(qǐng)求過(guò)程如下:
瀏覽器請(qǐng)求靜態(tài)資源demo.js
服務(wù)器讀取磁盤(pán)文件demo.js,返給瀏覽器,同時(shí)帶上文件的唯一標(biāo)識(shí)ETag
當(dāng)瀏覽器上的緩存文件過(guò)期時(shí),瀏覽器帶上請(qǐng)求頭If-None-Match(等于上一次請(qǐng)求的ETag)請(qǐng)求服務(wù)器
服務(wù)器比較請(qǐng)求頭里的If-None-Match和文件的ETag。如果一致就繼續(xù)使用本地緩存(304),如果不一致就再次返回文件內(nèi)容和ETag。
循環(huán)請(qǐng)求。。
const md5 = require("md5"); app.get("/demo.js",(req, res)=>{ let jsPath = path.resolve(__dirname,"./static/js/demo.js"); let cont = fs.readFileSync(jsPath); let etag = md5(cont); if(req.headers["if-none-match"] === etag){ res.writeHead(304, "Not Modified"); res.end(); } else { res.setHeader("ETag", etag); res.writeHead(200, "OK"); res.end(cont); } })
請(qǐng)求結(jié)果如下:
總結(jié)為了使緩存策略更加健壯、靈活,HTTP 1.0 版本 和 HTTP 1.1 版本的緩存策略會(huì)同時(shí)使用,甚至強(qiáng)制緩存和協(xié)商緩存也會(huì)同時(shí)使用,對(duì)于強(qiáng)制緩存,服務(wù)器通知瀏覽器一個(gè)緩存時(shí)間,在緩存時(shí)間內(nèi),下次請(qǐng)求,直接使用緩存,超出有效時(shí)間,執(zhí)行協(xié)商緩存策略,對(duì)于協(xié)商緩存,將緩存信息中的 Etag 和 Last-Modified 通過(guò)請(qǐng)求頭 If-None-Match 和 If-Modified-Since 發(fā)送給服務(wù)器,由服務(wù)器校驗(yàn)同時(shí)設(shè)置新的強(qiáng)制緩存,校驗(yàn)通過(guò)并返回 304 狀態(tài)碼時(shí),瀏覽器直接使用緩存,如果協(xié)商緩存也未命中,則服務(wù)器重新設(shè)置協(xié)商緩存的標(biāo)識(shí)。
關(guān)于Pragma當(dāng)該字段值為no-cache的時(shí)候,會(huì)告訴瀏覽器不要對(duì)該資源緩存,即每次都得向服務(wù)器發(fā)一次請(qǐng)求才行:
res.setHeader("Pragma", "no-cache") //禁止緩存 res.setHeader("Cache-Control", "public,max-age=120") //2分鐘
通過(guò)Pragma來(lái)禁止緩存,通過(guò)Cache-Control設(shè)置兩分鐘緩存,但是重新訪問(wèn)我們會(huì)發(fā)現(xiàn)瀏覽器會(huì)再次發(fā)起一次請(qǐng)求,說(shuō)明了Pragma的優(yōu)先級(jí)高于Cache-Control
緩存的優(yōu)先級(jí)Pragma > Cache-Control > Expires > ETag > Last-Modified
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/62037.html
摘要:緩存緩存,也叫網(wǎng)關(guān)緩存反向代理緩存。瀏覽器先向網(wǎng)關(guān)發(fā)起請(qǐng)求,網(wǎng)關(guān)服務(wù)器后面對(duì)應(yīng)著一臺(tái)或多臺(tái)負(fù)載均衡源服務(wù)器,會(huì)根據(jù)它們的負(fù)載請(qǐng)求,動(dòng)態(tài)將請(qǐng)求轉(zhuǎn)發(fā)到合適的源服務(wù)器上。雖然這種架構(gòu)負(fù)載均衡源服務(wù)器之間的緩存沒(méi)法共享,但卻擁有更好的處擴(kuò)展性。 一、前言? 工作上遇到一個(gè)這樣的需求,一個(gè)H5頁(yè)面在APP端,如果勾選已讀狀態(tài),則下次打開(kāi)該鏈接,會(huì)跳過(guò)此頁(yè)面。用到了HTML5 的本地存儲(chǔ) API ...
摘要:對(duì)于瀏覽器緩存,相信很多開(kāi)發(fā)者對(duì)它真的是又愛(ài)又恨。那么瀏覽器緩存究竟是個(gè)什么樣的神奇玩意呢什么是瀏覽器緩存簡(jiǎn)單來(lái)說(shuō),瀏覽器緩存就是把一個(gè)已經(jīng)請(qǐng)求過(guò)的資源如頁(yè)面,圖片,,數(shù)據(jù)等拷貝一份副本儲(chǔ)存在瀏覽器中。 對(duì)于瀏覽器緩存,相信很多開(kāi)發(fā)者對(duì)它真的是又愛(ài)又恨。一方面極大地提升了用戶體驗(yàn),而另一方面有時(shí)會(huì)因?yàn)樽x取了緩存而展示了錯(cuò)誤的東西,而在開(kāi)發(fā)過(guò)程中千方百計(jì)地想把緩存禁掉。那么瀏覽器緩存究竟...
摘要:對(duì)于瀏覽器緩存,相信很多開(kāi)發(fā)者對(duì)它真的是又愛(ài)又恨。那么瀏覽器緩存究竟是個(gè)什么樣的神奇玩意呢什么是瀏覽器緩存簡(jiǎn)單來(lái)說(shuō),瀏覽器緩存就是把一個(gè)已經(jīng)請(qǐng)求過(guò)的資源如頁(yè)面,圖片,,數(shù)據(jù)等拷貝一份副本儲(chǔ)存在瀏覽器中。 對(duì)于瀏覽器緩存,相信很多開(kāi)發(fā)者對(duì)它真的是又愛(ài)又恨。一方面極大地提升了用戶體驗(yàn),而另一方面有時(shí)會(huì)因?yàn)樽x取了緩存而展示了錯(cuò)誤的東西,而在開(kāi)發(fā)過(guò)程中千方百計(jì)地想把緩存禁掉。那么瀏覽器緩存究竟...
摘要:緩存機(jī)制緩存機(jī)制主要由以下三部分組成緩存存儲(chǔ)策略這個(gè)策略的作用只有一個(gè),用于決定響應(yīng)內(nèi)容是否可緩存到客戶端。如果判斷標(biāo)識(shí)無(wú)效,則返回,用新數(shù)據(jù)替換客戶端緩存。表示文件在本地應(yīng)該緩存,且有效時(shí)長(zhǎng)是秒從發(fā)出請(qǐng)求算起。 HTTP緩存機(jī)制 HTTP緩存機(jī)制主要由以下三部分組成 緩存存儲(chǔ)策略 這個(gè)策略的作用只有一個(gè),用于決定 Http 響應(yīng)內(nèi)容是否可緩存到客戶端。主要通過(guò)Cache-Contro...
瀏覽器緩存機(jī)制學(xué)習(xí)總結(jié) 最近在做一個(gè)考試系統(tǒng)時(shí),由于經(jīng)常加載試卷或圖片等等靜態(tài)資源,抽空學(xué)習(xí)了一下緩存機(jī)制,在此記錄 為什么要使用緩存 1、通過(guò)HTTP協(xié)議,在客戶端和瀏覽器建立連接時(shí)需要消耗時(shí)間,而大的響應(yīng)需要在客戶端和服務(wù)器之間進(jìn)行多次往返通信才能獲得完整的響應(yīng),這拖延了瀏覽器可以使用和處理內(nèi)容的時(shí)間。這就增加了訪問(wèn)服務(wù)器的數(shù)據(jù)和資源的成本,因此利用瀏覽器的緩存機(jī)制重用以前獲取的數(shù)據(jù)就變成了性...
閱讀 3235·2021-11-11 16:55
閱讀 2502·2021-10-13 09:39
閱讀 2428·2021-09-13 10:27
閱讀 2164·2019-08-30 15:55
閱讀 3094·2019-08-30 15:54
閱讀 3138·2019-08-29 16:34
閱讀 1831·2019-08-29 12:41
閱讀 1073·2019-08-29 11:33