前言
[實(shí)踐系列] 主要是讓我們通過實(shí)踐去加深對(duì)一些原理的理解溜腐。
有興趣的同學(xué)可以關(guān)注 [實(shí)踐系列] 译株。 求star求follow~
如果覺得自己已經(jīng)掌握瀏覽器緩存機(jī)制知識(shí)的同學(xué),可以直接看實(shí)踐部分哈~
目錄
1. DNS 緩存 // 雖說跟標(biāo)題關(guān)系不大,了解一下也不錯(cuò)
2. CDN 緩存 // 雖說跟標(biāo)題關(guān)系不大,了解一下也不錯(cuò)
3. 瀏覽器緩存 // 本文將重點(diǎn)介紹并實(shí)踐
DNS 緩存
什么是DNS
全稱 Domain Name System ,即域名系統(tǒng)。
萬維網(wǎng)上作為域名和IP地址相互映射的一個(gè)分布式數(shù)據(jù)庫挺益,能夠使用戶更方便的訪問互聯(lián)網(wǎng)歉糜,而不用去記住能夠被機(jī)器直接讀取的IP數(shù)串。DNS協(xié)議運(yùn)行在UDP協(xié)議之上矩肩,使用端口號(hào)53现恼。
DNS解析
簡單的說,通過域名,最終得到該域名對(duì)應(yīng)的IP地址的過程叫做域名解析(或主機(jī)名解析)肃续。
www.dnscache.com (域名) - DNS解析 -> 11.222.33.444 (IP地址)
DNS緩存
有dns的地方,就有緩存。瀏覽器叉袍、操作系統(tǒng)始锚、Local DNS、根域名服務(wù)器喳逛,它們都會(huì)對(duì)DNS結(jié)果做一定程度的緩存瞧捌。
DNS查詢過程如下:
首先搜索瀏覽器自身的DNS緩存,如果存在,則域名解析到此完成润文。
如果瀏覽器自身的緩存里面沒有找到對(duì)應(yīng)的條目姐呐,那么會(huì)嘗試讀取操作系統(tǒng)的hosts文件看是否存在對(duì)應(yīng)的映射關(guān)系,如果存在,則域名解析到此完成典蝌。
如果本地hosts文件不存在映射關(guān)系曙砂,則查找本地DNS服務(wù)器(ISP服務(wù)器,或者自己手動(dòng)設(shè)置的DNS服務(wù)器),如果存在,域名到此解析完成。
如果本地DNS服務(wù)器還沒找到的話,它就會(huì)向根服務(wù)器發(fā)出請(qǐng)求,進(jìn)行遞歸查詢骏掀。
CDN 緩存
什么是CDN
全稱 Content Delivery Network,即內(nèi)容分發(fā)網(wǎng)絡(luò)鸠澈。
摘錄一個(gè)形象的比喻,來理解CDN是什么。
10年前截驮,還沒有火車票代售點(diǎn)一說笑陈,12306.cn更是無從說起。那時(shí)候火車票還只能在火車站的售票大廳購買葵袭,而我所在的小縣城并不通火車涵妥,火車票都要去市里的火車站購買,而從我家到縣城再到市里坡锡,來回就是4個(gè)小時(shí)車程蓬网,簡直就是浪費(fèi)生命。后來就好了娜氏,小縣城里出現(xiàn)了火車票代售點(diǎn)拳缠,甚至鄉(xiāng)鎮(zhèn)上也有了代售點(diǎn),可以直接在代售點(diǎn)購買火車票贸弥,方便了不少,全市人民再也不用在一個(gè)點(diǎn)苦逼的排隊(duì)買票了海渊。
簡單的理解CDN就是這些代售點(diǎn)(緩存服務(wù)器)的承包商,他為買票者提供了便利,幫助他們?cè)谧罱牡胤?最近的CDN節(jié)點(diǎn))用最短的時(shí)間(最短的請(qǐng)求時(shí)間)買到票(拿到資源),這樣去火車站售票大廳排隊(duì)的人也就少了绵疲。也就減輕了售票大廳的壓力(起到分流作用,減輕服務(wù)器負(fù)載壓力)。
用戶在瀏覽網(wǎng)站的時(shí)候臣疑,CDN會(huì)選擇一個(gè)離用戶最近的CDN邊緣節(jié)點(diǎn)來響應(yīng)用戶的請(qǐng)求盔憨,這樣海南移動(dòng)用戶的請(qǐng)求就不會(huì)千里迢迢跑到北京電信機(jī)房的服務(wù)器(假設(shè)源站部署在北京電信機(jī)房)上了排宰。
CDN緩存
關(guān)于CDN緩存,在瀏覽器本地緩存失效后,瀏覽器會(huì)向CDN邊緣節(jié)點(diǎn)發(fā)起請(qǐng)求亚茬。類似瀏覽器緩存,CDN邊緣節(jié)點(diǎn)也存在著一套緩存機(jī)制。CDN邊緣節(jié)點(diǎn)緩存策略因服務(wù)商不同而不同睦尽,但一般都會(huì)遵循h(huán)ttp標(biāo)準(zhǔn)協(xié)議,通過http響應(yīng)頭中的
Cache-control: max-age //后面會(huì)提到
的字段來設(shè)置CDN邊緣節(jié)點(diǎn)數(shù)據(jù)緩存時(shí)間问慎。
當(dāng)瀏覽器向CDN節(jié)點(diǎn)請(qǐng)求數(shù)據(jù)時(shí)萍摊,CDN節(jié)點(diǎn)會(huì)判斷緩存數(shù)據(jù)是否過期,若緩存數(shù)據(jù)并沒有過期如叼,則直接將緩存數(shù)據(jù)返回給客戶端冰木;否則,CDN節(jié)點(diǎn)就會(huì)向服務(wù)器發(fā)出回源請(qǐng)求笼恰,從服務(wù)器拉取最新數(shù)據(jù)踊沸,更新本地緩存,并將最新數(shù)據(jù)返回給客戶端社证。 CDN服務(wù)商一般會(huì)提供基于文件后綴逼龟、目錄多個(gè)維度來指定CDN緩存時(shí)間,為用戶提供更精細(xì)化的緩存管理追葡。
CDN 優(yōu)勢(shì)
- CDN節(jié)點(diǎn)解決了跨運(yùn)營商和跨地域訪問的問題腺律,訪問延時(shí)大大降低。
- 大部分請(qǐng)求在CDN邊緣節(jié)點(diǎn)完成辽俗,CDN起到了分流作用疾渣,減輕了源服務(wù)器的負(fù)載。
瀏覽器緩存(http緩存)
對(duì)著這張圖先發(fā)呆30秒~
什么是瀏覽器緩存
簡單來說,瀏覽器緩存其實(shí)就是瀏覽器保存通過HTTP獲取的所有資源,是瀏覽器將網(wǎng)絡(luò)資源存儲(chǔ)在本地的一種行為崖飘。
緩存的資源去哪里了?
你可能會(huì)有疑問,瀏覽器存儲(chǔ)了資源,那它把資源存儲(chǔ)在哪里呢榴捡?
memory cache
MemoryCache顧名思義,就是將資源緩存到內(nèi)存中朱浴,等待下次訪問時(shí)不需要重新下載資源吊圾,而直接從內(nèi)存中獲取。Webkit早已支持memoryCache翰蠢。
目前Webkit資源分成兩類项乒,一類是主資源,比如HTML頁面梁沧,或者下載項(xiàng)檀何,一類是派生資源,比如HTML頁面中內(nèi)嵌的圖片或者腳本鏈接廷支,分別對(duì)應(yīng)代碼中兩個(gè)類:MainResourceLoader和SubresourceLoader频鉴。雖然Webkit支持memoryCache,但是也只是針對(duì)派生資源恋拍,它對(duì)應(yīng)的類為CachedResource垛孔,用于保存原始數(shù)據(jù)(比如CSS,JS等)施敢,以及解碼過的圖片數(shù)據(jù)周荐。
disk cache
DiskCache顧名思義狭莱,就是將資源緩存到磁盤中,等待下次訪問時(shí)不需要重新下載資源概作,而直接從磁盤中獲取腋妙,它的直接操作對(duì)象為CurlCacheManager。
- |memory cache | disk cache
:---:|:---:|:---:
相同點(diǎn) |只能存儲(chǔ)一些派生類資源文件 | 只能存儲(chǔ)一些派生類資源文件
不同點(diǎn) |退出進(jìn)程時(shí)數(shù)據(jù)會(huì)被清除 | 退出進(jìn)程時(shí)數(shù)據(jù)不會(huì)被清除
存儲(chǔ)資源|一般腳本仆嗦、字體辉阶、圖片會(huì)存在內(nèi)存當(dāng)中|一般非腳本會(huì)存在內(nèi)存當(dāng)中,如css等
因?yàn)镃SS文件加載一次就可渲染出來,我們不會(huì)頻繁讀取它,所以它不適合緩存到內(nèi)存中,但是js之類的腳本卻隨時(shí)可能會(huì)執(zhí)行,如果腳本在磁盤當(dāng)中,我們?cè)趫?zhí)行腳本的時(shí)候需要從磁盤取到內(nèi)存中來,這樣IO開銷就很大了,有可能導(dǎo)致瀏覽器失去響應(yīng)瘩扼。
三級(jí)緩存原理 (訪問緩存優(yōu)先級(jí))
- 先在內(nèi)存中查找,如果有,直接加載谆甜。
- 如果內(nèi)存中不存在,則在硬盤中查找,如果有直接加載。
- 如果硬盤中也沒有,那么就進(jìn)行網(wǎng)絡(luò)請(qǐng)求集绰。
- 請(qǐng)求獲取的資源緩存到硬盤和內(nèi)存规辱。
瀏覽器緩存的分類
強(qiáng)緩存
協(xié)商緩存
瀏覽器再向服務(wù)器請(qǐng)求資源時(shí),首先判斷是否命中強(qiáng)緩存,再判斷是否命中協(xié)商緩存!
瀏覽器緩存的優(yōu)點(diǎn)
1.減少了冗余的數(shù)據(jù)傳輸
2.減少了服務(wù)器的負(fù)擔(dān),大大提升了網(wǎng)站的性能
3.加快了客戶端加載網(wǎng)頁的速度
強(qiáng)緩存
瀏覽器在加載資源時(shí)栽燕,會(huì)先根據(jù)本地緩存資源的 header 中的信息判斷是否命中強(qiáng)緩存罕袋,如果命中則直接使用緩存中的資源不會(huì)再向服務(wù)器發(fā)送請(qǐng)求。
這里的 header 中的信息指的是 expires 和 cahe-control.
Expires
該字段是 http1.0 時(shí)的規(guī)范碍岔,它的值為一個(gè)絕對(duì)時(shí)間的 GMT 格式的時(shí)間字符串浴讯,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT。這個(gè)時(shí)間代表著這個(gè)資源的失效時(shí)間蔼啦,在此時(shí)間之前榆纽,即命中緩存。這種方式有一個(gè)明顯的缺點(diǎn)捏肢,由于失效時(shí)間是一個(gè)絕對(duì)時(shí)間奈籽,所以當(dāng)服務(wù)器與客戶端時(shí)間偏差較大時(shí),就會(huì)導(dǎo)致緩存混亂鸵赫。
Cache-Control
Cache-Control 是 http1.1 時(shí)出現(xiàn)的 header 信息衣屏,主要是利用該字段的 max-age 值來進(jìn)行判斷,它是一個(gè)相對(duì)時(shí)間辩棒,例如 Cache-Control:max-age=3600狼忱,代表著資源的有效期是 3600 秒。cache-control 除了該字段外一睁,還有下面幾個(gè)比較常用的設(shè)置值:
no-cache:需要進(jìn)行協(xié)商緩存藕赞,發(fā)送請(qǐng)求到服務(wù)器確認(rèn)是否使用緩存。
no-store:禁止使用緩存卖局,每一次都要重新請(qǐng)求數(shù)據(jù)。
public:可以被所有的用戶緩存双霍,包括終端用戶和 CDN 等中間代理服務(wù)器砚偶。
private:只能被終端用戶的瀏覽器緩存批销,不允許 CDN 等中繼緩存服務(wù)器對(duì)其緩存。
Cache-Control 與 Expires 可以在服務(wù)端配置同時(shí)啟用染坯,同時(shí)啟用的時(shí)候 Cache-Control 優(yōu)先級(jí)高均芽。
協(xié)商緩存
當(dāng)強(qiáng)緩存沒有命中的時(shí)候,瀏覽器會(huì)發(fā)送一個(gè)請(qǐng)求到服務(wù)器单鹿,服務(wù)器根據(jù) header 中的部分信息來判斷是否命中緩存掀宋。如果命中,則返回 304 仲锄,告訴瀏覽器資源未更新劲妙,可使用本地的緩存。
這里的 header 中的信息指的是 Last-Modify/If-Modify-Since 和 ETag/If-None-Match.
Last-Modify/If-Modify-Since
瀏覽器第一次請(qǐng)求一個(gè)資源的時(shí)候儒喊,服務(wù)器返回的 header 中會(huì)加上 Last-Modify镣奋,Last-modify 是一個(gè)時(shí)間標(biāo)識(shí)該資源的最后修改時(shí)間。
當(dāng)瀏覽器再次請(qǐng)求該資源時(shí)怀愧,request 的請(qǐng)求頭中會(huì)包含 If-Modify-Since侨颈,該值為緩存之前返回的 Last-Modify。服務(wù)器收到 If-Modify-Since 后芯义,根據(jù)資源的最后修改時(shí)間判斷是否命中緩存哈垢。
如果命中緩存,則返回 304扛拨,并且不會(huì)返回資源內(nèi)容耘分,并且不會(huì)返回 Last-Modify。
缺點(diǎn):
短時(shí)間內(nèi)資源發(fā)生了改變鬼癣,Last-Modified 并不會(huì)發(fā)生變化陶贼。
周期性變化。如果這個(gè)資源在一個(gè)周期內(nèi)修改回原來的樣子了待秃,我們認(rèn)為是可以使用緩存的拜秧,但是 Last-Modified 可不這樣認(rèn)為,因此便有了 ETag。
ETag/If-None-Match
與 Last-Modify/If-Modify-Since 不同的是章郁,Etag/If-None-Match 返回的是一個(gè)校驗(yàn)碼枉氮。ETag 可以保證每一個(gè)資源是唯一的,資源變化都會(huì)導(dǎo)致 ETag 變化暖庄。服務(wù)器根據(jù)瀏覽器上送的 If-None-Match 值來判斷是否命中緩存聊替。
與 Last-Modified 不一樣的是,當(dāng)服務(wù)器返回 304 Not Modified 的響應(yīng)時(shí)培廓,由于 ETag 重新生成過惹悄,response header 中還會(huì)把這個(gè) ETag 返回,即使這個(gè) ETag 跟之前的沒有變化肩钠。
Last-Modified 與 ETag 是可以一起使用的泣港,服務(wù)器會(huì)優(yōu)先驗(yàn)證 ETag暂殖,一致的情況下,才會(huì)繼續(xù)比對(duì) Last-Modified当纱,最后才決定是否返回 304呛每。
總結(jié)
當(dāng)瀏覽器再次訪問一個(gè)已經(jīng)訪問過的資源時(shí),它會(huì)這樣做:
1.看看是否命中強(qiáng)緩存坡氯,如果命中晨横,就直接使用緩存了。
2.如果沒有命中強(qiáng)緩存箫柳,就發(fā)請(qǐng)求到服務(wù)器檢查是否命中協(xié)商緩存手形。
3.如果命中協(xié)商緩存,服務(wù)器會(huì)返回 304 告訴瀏覽器使用本地緩存滞时。
4.否則叁幢,返回最新的資源。
實(shí)踐加深理解
talk is cheap , show me the code 坪稽。讓我們通過實(shí)踐得真知~
在實(shí)踐時(shí),注意瀏覽器控制臺(tái)Network的
以下我們只對(duì)強(qiáng)緩存的Cache-Control和協(xié)商緩存的ETag進(jìn)行實(shí)踐,其他小伙伴們可以自己實(shí)踐~
package.json
{
"name": "webcache",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"cache": "nodemon ./index.js"
},
"author": "webfansplz",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.2.3",
"@babel/register": "^7.0.0",
"koa": "^2.6.2",
"koa-static": "^5.0.0"
},
"dependencies": {
"nodemon": "^1.18.9"
}
}
.babelrc
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
index.js
require('@babel/register');
require('./webcache.js');
webcache.js
import Koa from 'koa';
import path from 'path';
//靜態(tài)資源中間件
import resource from 'koa-static';
const app = new Koa();
const host = 'localhost';
const port = 4396;
app.use(resource(path.join(__dirname, './static')));
app.listen(port, () => {
console.log(`server is listen in ${host}:${port}`);
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>前端緩存</title>
<style>
.web-cache img {
display: block;
width: 100%;
}
</style>
</head>
<body>
<div class="web-cache"><img src="./web.png" /></div>
</body>
</html>
我們用koa先起個(gè)web服務(wù)器,然后用koa-static這個(gè)中間件做靜態(tài)資源配置,并在static文件夾下放了index.html和web.png。
Ok,接下來我們來啟動(dòng)服務(wù)窒百。
npm run cache
server is listen in localhost:4396黍判。
接下來我們打開瀏覽器輸入地址:
localhost:4396
完美~(哈哈,豬仔別噴我,純屬娛樂效果)
Ok!!!接下來我們來實(shí)踐下強(qiáng)緩存。~
Cache-Control
webcache.js
import Koa from 'koa';
import path from 'path';
//靜態(tài)資源中間件
import resource from 'koa-static';
const app = new Koa();
const host = 'localhost';
const port = 4396;
app.use(async (ctx, next) => {
// 設(shè)置響應(yīng)頭Cache-Control 設(shè)置資源有效期為300秒
ctx.set({
'Cache-Control': 'max-age=300'
});
await next();
});
app.use(resource(path.join(__dirname, './static')));
app.listen(port, () => {
console.log(`server is listen in ${host}:${port}`);
});
我們刷新頁面可以看到響應(yīng)頭的Cache-Control變成了max-age=300篙梢。
我們順便來驗(yàn)證下三級(jí)緩存原理
我們剛進(jìn)行了網(wǎng)絡(luò)請(qǐng)求,瀏覽器把web.png存進(jìn)了磁盤和內(nèi)存中顷帖。
根據(jù)三級(jí)緩存原理,我們會(huì)先在內(nèi)存中找資源,我們來刷新頁面渤滞。
我們?cè)诩t線部分看到了, from memory cache贬墩。nice~
ok,接下來,我們關(guān)掉該頁面,再重新打開妄呕。因?yàn)閮?nèi)存是存在進(jìn)程中的,所以關(guān)閉該頁面,內(nèi)存中的資源也被釋放掉了,磁盤中的資源是永久性的,所以還存在陶舞。
根據(jù)三級(jí)緩存原理,如果在內(nèi)存中沒找到資源,便會(huì)去磁盤中尋找!
from disk cache !!! ok,以上也就驗(yàn)證了三級(jí)緩存原理,相信你對(duì)緩存資源的存儲(chǔ)也有了更深的理解了绪励。
我們剛對(duì)資源設(shè)置的有效期是300秒,我們接下來來驗(yàn)證緩存是否失效肿孵。
300秒后。疏魏。停做。
我們通過返回值可以看到,緩存失效了。
通過以上實(shí)踐,你是否對(duì)強(qiáng)緩存有了更深入的理解了呢?
Ok!!!接下來我們來實(shí)踐下協(xié)商緩存大莫。~
由于Cache-Control的默認(rèn)值是private(只能被終端用戶的瀏覽器緩存蛉腌,不允許 CDN 等中繼緩存服務(wù)器對(duì)其緩存。),所以我們這里不用對(duì)Cache-Control進(jìn)行設(shè)置!
ETag
//ETag support for Koa responses using etag.
npm install koa-etag -D
// etag works together with conditional-get
npm install koa-conditional-get -D
我們這里直接使用現(xiàn)成的插件幫我們計(jì)算文件的ETag值,站在巨人的肩膀上!
webcache.js
import Koa from 'koa';
import path from 'path';
//靜態(tài)資源中間件
import resource from 'koa-static';
import conditional from 'koa-conditional-get';
import etag from 'koa-etag';
const app = new Koa();
const host = 'localhost';
const port = 4396;
// etag works together with conditional-get
app.use(conditional());
app.use(etag());
app.use(resource(path.join(__dirname, './static')));
app.listen(port, () => {
console.log(`server is listen in ${host}:${port}`);
});
ok眉抬。第一次請(qǐng)求.
我們發(fā)現(xiàn)返回值里面已經(jīng)有了Etag值贯吓。
接下來再請(qǐng)求的時(shí)候,瀏覽器將會(huì)帶上If-None-Match請(qǐng)求頭,并賦值為上一次返回頭的Etag值,然后與 這次返回值的Etag值進(jìn)行對(duì)比。如果一致則命中協(xié)商緩存蜀变。返回304 Not Modified。接下來我們來驗(yàn)證一下~
ok,如圖所示,完美驗(yàn)證了上面的說法介评。
接下來我們修改web.png ,來驗(yàn)證是否資源改變時(shí) 協(xié)商緩存策略也就失效呢库北?
如圖所示.協(xié)商緩存的實(shí)踐也驗(yàn)證了原理。
大功告成
寫文章真的是件挺累的事,如果覺得有幫助到你,請(qǐng)給star/follow 支持下作者~