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