瀏覽器和網絡篇(一)--瀏覽器的緩存機制

http 全過程

輸入url,從url解析出域名-->DNS映射爲IP-->TCP三次握手(完成TLS/SSL握手)-->構造HTTP請求,填充上下文到HTTP首部--> 發起HTTP請求-->HTTP響應-->(瀏覽器跟蹤重定向地址)-->服務器處理請求-->服務器返回一個html響應-->(視狀況決定釋放TCP鏈接)-->瀏覽器引擎解析HTML響應,渲染包體到用戶界面-->獲取頁面內資源的HTTP請求。css

image.png

DNS 緩存

什麼是DNS

全稱 Domain Name System ,即域名系統。html

  • 萬維網上做爲域名和IP地址相互映射的一個分佈式數據庫,可以使用戶更方便的訪問互聯網,而不用去記住可以被機器直接讀取的IP數串。
  • DNS協議運行在UDP協議之上,使用端口號53。

dns解析

經過域名,最終獲得該域名對應的IP地址的過程叫作域名解析(或主機名解析)git

www.dnscache.com (域名) - DNS解析 -> 11.222.33.444 (IP地址)github

dns緩存

瀏覽器、操做系統、本地Local DNS、根域名服務器,它們都會對DNS結果作必定程度的緩存。 dns查詢過程:算法

  • 一、首先搜索瀏覽器自身的DNS緩存,若是存在,則域名解析到此完成。
  • 二、瀏覽器自身的緩存裏面沒有就會嘗試讀取操做系統的hosts文件看是否存在對應的映射關係,若是存在,則域名解析到此完成。
  • 三、若是本地hosts文件不存在映射關係,則查找本地DNS服務器(ISP服務器,或者本身手動設置的DNS服務器),若是存在,域名到此解析完成。
  • 若是本地DNS服務器還沒找到的話,它就會向根服務器發出請求,進行遞歸查詢

CDN 緩存

什麼是CDN?

全稱 Content Delivery Network,即內容分發網絡. 用戶在瀏覽網站的時候,CDN會選擇一個離用戶最近的CDN邊緣節點來響應用戶的請求,訪問延時大大下降,這樣能夠起到分流做用,減輕服務器負載壓力。chrome

CDN緩存

  • 關於CDN緩存,在瀏覽器本地緩存失效後,瀏覽器會向CDN邊緣節點發起請求。數據庫

  • 相似瀏覽器緩存,CDN邊緣節點也存在着一套緩存機制。CDN邊緣節點緩存策略因服務商不一樣而不一樣,但通常都會遵循http標準協議,經過http響應頭中的 Cache-control: max-age 的字段來設置CDN邊緣節點數據緩存時間.瀏覽器

  • 當瀏覽器向CDN節點請求數據時,CDN節點會判斷緩存數據是否過時,若緩存數據並無過時,則直接將緩存數據返回給客戶端;緩存

  • 不然,CDN節點就會向服務器發出回源請求,從服務器拉取最新數據,更新本地緩存,並將最新數據返回給客戶端。 CDN服務商通常會提供基於文件後綴、目錄多個維度來指定CDN緩存時間,爲用戶提供更精細化的緩存管理。安全

CDN 優點

CDN節點解決了跨運營商和跨地域訪問的問題,訪問延時大大下降。 大部分請求在CDN邊緣節點完成,CDN起到了分流做用,減輕了源服務器的負載。

瀏覽器緩存(http緩存)

1.png 瀏覽器與服務器通訊的方式爲應答模式,便是:瀏覽器發起HTTP請求 – 服務器響應該請求。

  • 那麼瀏覽器怎麼肯定一個資源該不應緩存,如何去緩存呢?
  • 瀏覽器第一次向服務器發起該請求後拿到請求結果後,將請求結果和緩存標識存入瀏覽器緩存,瀏覽器對於緩存的處理是根據第一次請求資源時返回的響應頭來肯定的。具體過程以下圖:

image.png

瀏覽器每次發起請求,都會先在瀏覽器緩存中查找該請求的結果以及緩存標識 瀏覽器每次拿到返回的請求結果都會將該結果和緩存標識存入瀏覽器緩存中

瀏覽器緩存的優勢

1.減小了冗餘的數據傳輸 好比直接使用緩存而不發起請求

2.減小了服務器的負擔,大大提高了網站的性能,好比發起了請求可是和服務器緩存數據一致不須要從新響應

3.加快了客戶端加載網頁的速度 好比一個數據請求,能夠分爲瀏覽器發起請求、服務求響應請求、瀏覽器解析響應三個步驟。瀏覽器緩存能夠幫助咱們在第一和第二步驟中優化性能,

緩存位置分類

從緩存位置上來講分爲四種,而且各自有優先級,當依次查找緩存且都沒有命中的時候,纔會去請求網絡。

  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache

1.Service Worker

  • Service Worker: 是運行在瀏覽器背後的獨立線程,通常能夠用來實現緩存功能。
  • 傳輸協議 HTTPS : Service Worker涉及請求攔截,必須使用 HTTPS 協議來保障安全。
  • 能夠自由控制緩存哪些文件、如何匹配緩存、如何讀取緩存,而且緩存是持續性的。

Service Worker 實現緩存功能通常分爲三個步驟:

一、首先註冊 Service Worker。

二、監聽到install 事件: 緩存須要的文件,下次訪問時候經過攔截請求查詢是否存在緩存,存在緩存的話就能夠直接讀取緩存文件,不然就去請求數據。

三、當 Service Worker 沒有命中緩存的時候,調用 fetch 函數獲取數據,根據緩存查找優先級去查找數據。 四、Memory Cache或則網絡請求中獲取的數據,瀏覽器仍是會顯示咱們是從 Service Worker 中獲取的內容。

2.Memory Cache 內存緩存

  • 讀取數據確定比磁盤快,高效,可是緩存持續性很短,會隨着進程(好比關閉tab頁面)的釋放而釋放,
  • 容量小,操做系統須要精打細算內存的使用。
  • 二次刷新頁面的數據大都來自於內存緩存

目前Webkit資源分紅兩類:

  • 一類是主資源,好比HTML頁面,或者下載項
  • 一類是派生資源,好比HTML頁面中內嵌的圖片或者腳本連接
  • 分別對應代碼中兩個類:MainResourceLoader和SubresourceLoader。

雖然Webkit支持memoryCache,可是也只是針對派生資源,它對應的類爲CachedResource,用於保存原始數據(好比CSS,JS等),以及解碼過的圖片數據。

一、內存緩存資源指令:preloader相關指令 (例如<link rel="prefetch">)下載資源, 是頁面優化的常見手段之一解析js/css文件,邊網絡請求下一個資源

二、內存緩存資源時和HTTP緩存頭Cache-Control關係不大,資源的匹配 除了對URL作匹配,還可能會對Content-TypeCORS等其餘特徵作校驗。

3.Disk Cache

Disk Cache

  • 存儲在硬盤中,讀取慢,什麼都能存儲,比Memory Cache效率低可是容量大。
  • 覆蓋面大。
  • 根據 HTTP Herder 中的字段判斷哪些資源須要緩存,哪些資源能夠不請求直接使用,哪些資源已通過期須要從新請求,它的直接操做對象爲CurlCacheManager
  • 跨站點的狀況下,相同地址的資源一旦被硬盤緩存下來,就不會再次去請求數據。
  • 絕大部分的緩存都來自 Disk Cache

文件通常優先存儲進硬盤,對於系統優先級和使用率高的相對較小的資源存儲在內存,

image.png

由於CSS文件加載一次就可渲染出來,咱們不會頻繁讀取它,因此它不適合緩存到內存中,可是js之類的腳本卻隨時可能會執行,若是腳本在磁盤當中,咱們在執行腳本的時候須要從磁盤取到內存中來,這樣IO開銷就很大了,有可能致使瀏覽器失去響應。

4.Push Cache

  • Push Cache(推送緩存)是 HTTP/2 中的內容,當以上三種緩存都沒有命中時,它纔會被使用。

  • 它只在會話(Session)中存在,一旦會話結束就被釋放,而且緩存時間也很短暫,在Chrome瀏覽器中只有5分鐘左右

  • 不嚴格執行HTTP頭中的緩存指令, 能夠推送 no-cache 和 no-store 的資源,一旦鏈接被關閉,Push Cache 就被釋放

  • 全部的資源都能被推送,而且可以被緩存

  • Edge 和 Safari 瀏覽器支持相對比較差

  • 多個頁面可使用同一個HTTP/2的鏈接,也就可使用同一個Push Cache。這主要仍是依賴瀏覽器的實現而定,出於對性能的考慮,有的瀏覽器會對相同域名但不一樣的tab標籤使用同一個HTTP鏈接。

  • Push Cache 中的緩存只能被使用一次

  • 瀏覽器能夠拒絕接受已經存在的資源推送,能夠給其餘域名推送資源。

若是以上四種緩存都沒有命中的話,那麼只能發起請求來獲取資源了。

三級緩存原理 (訪問緩存優先級)

  • 先在內存中查找,若是有,直接加載。
  • 若是內存中不存在,則在硬盤中查找,若是有直接加載。
  • 若是硬盤中也沒有,那麼就進行網絡請求。
  • 請求獲取的資源緩存到硬盤和內存。

緩存策略分類

那麼爲了性能上的考慮,大部分的接口都應該選擇好緩存策略,一般瀏覽器緩存策略分爲兩種:強緩存和協商緩存 緩存策略都是經過設置 HTTP Header 來實現的。 瀏覽器再向服務器請求資源時,首先判斷是否命中強緩存,再判斷是否命中協商緩存!

強緩存

瀏覽器在加載資源時,會先根據本地緩存資源的 header 中的信息判斷是否命中強緩存,若是命中則直接使用緩存中的資源不會再向服務器發送請求, 在chrome控制檯的Network選項中能夠看到該請求返回200的狀態碼,而且Size顯示from disk cache或from memory cache。

這裏的 header 中的信息指的是 expires 和 cahe-control.

Expires

該字段是 http1.0 時的規範,它的值爲一個絕對時間的 GMT 格式的時間字符串,好比 Expires:Mon,18 Oct 2066 23:59:59 GMT。這個時間表明着這個資源的失效時間,在此時間以前,即命中緩存。這種方式有一個明顯的缺點,因爲失效時間是一個絕對時間,因此當服務器與客戶端時間誤差較大時,就會致使緩存混亂。

  • 緩存過時時間,用來指定資源到期的時間,是服務器端的具體的時間點。

  • Expires=max-age + 請求時間,須要和Last-modified結合使用。

  • Expires是Web服務器響應消息頭字段,在響應http請求時告訴瀏覽器在過時時間前瀏覽器能夠直接從瀏覽器緩存取數據,而無需再次請求。

  • 該字段是 http1.0 時的規範,它的值爲一個絕對時間的 GMT 格式的時間字符串,好比 Expires:Mon,18 Oct 2066 23:59:59 GMT。這個時間表明着這個資源的失效時間,在此時間以前,即命中緩存

  • 受限於本地時間,若是修改了本地時間,可能會形成服務器與客戶端時間誤差較大時,就會致使緩存混亂。

Cache-Control

  • Cache-Control 是 http1.1 時出現的 header 信息,主要用於控制網頁緩存。

  • 好比 Cache-Control:max-age=3600,表明着資源的有效期是 3600 秒(瀏覽器也會記錄下來),這個時間內再次加載資源,就會命中強緩存。

  • Cache-Control 能夠在請求頭或者響應頭中設置,而且能夠組合使用多種指令:

image.png

public:全部內容都將被緩存(客戶端和代理服務器均可緩存)。具體來講響應可被任何中間節點緩存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中間的proxy能夠緩存資源。

private:全部內容只有客戶端能夠緩存,Cache-Control的默認取值。具體來講,表示中間節點不容許緩存,對於Browser <-- proxy1 <-- proxy2 <-- Server,proxy 會把Server 返回的數據發送給proxy1,本身不緩存任何數據,只作請求轉發。

no-cache:客戶端緩存內容須要通過協商緩存來驗證決定是否使用。 表示不使用 Cache-Control的緩存控制方式作前置驗證,而是使用 Etag 或者Last-Modified字段來控制緩存,瀏覽器在使用緩存數據時,須要先確認一下數據是否還跟服務器保持一致

no-store:全部內容都不會被緩存,即不使用強制緩存,也不使用協商緩存

max-age:max-age=xxx (xxx is numeric)表示緩存內容將在xxx秒後失效

s-maxage(單位爲s):同max-age做用同樣,只在代理服務器中生效(好比CDN緩存)。而s-maxage用於代理緩存。s-maxage的優先級高於max-age。若是存在s-maxage,則會覆蓋掉max-age和Expires header。

max-stale:能容忍的最大過時時間。客戶端能夠接收一個已通過期了的響應。若是沒有指定,接收任何age的響應(age表示響應由源站生成或確認的時間與當前時間的差值)。

min-fresh:可以容忍的最小新鮮度。客戶端能夠接受age加上min-fresh設定的時間之和的響應。

image.png

Cache-Control 與 Expires 能夠在服務端配置同時啓用,同時啓用的時候 Cache-Control 優先級高

協商緩存

當強緩存沒有命中的時候,瀏覽器會發送一個請求到服務器,服務器根據 header 中的部分信息來判斷是否命中緩存。若是命中,則返回 304和Not Modified ,告訴瀏覽器資源未更新,可以使用本地的緩存。

image.png

協商緩存失效,返回200和請求結果:

image.png

這裏的 header 中的信息指的是 Last-Modify/If-Modify-Since 和 ETag/If-None-Match.

Last-Modify/If-Modify-Since

瀏覽器第一次請求一個資源的時候,服務器返回的 response header 中會加上 Last-Modify,Last-modify 是一個時間標識該資源的最後修改時間: Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT

當瀏覽器再次請求該資源時,request 的請求頭中會包含 If-Modify-Since,該值爲緩存以前返回的 Last-Modify。服務器收到 If-Modify-Since 後,根據資源的最後修改時間判斷是否命中緩存。

若是命中緩存,則返回 304,而且不會返回資源內容,而且不會返回 Last-Modify。If-Modified-Since的時間小於服務器中這個資源的最後修改時間,說明文件有更新,因而返回新的資源文件和200。

image.png

缺點:

Last-Modified 只能以秒計時,短期內資源發生了改變,Last-Modified 並不會發生變化,服務端不能命中緩存致使發送相同的資源.

週期性變化。若是這個資源在一個週期內修改回原來的樣子了,咱們認爲是可使用緩存的,可是 Last-Modified 可不這樣認爲,所以便有了 ETag。

ETag/If-None-Match

  • Etag是服務器響應請求時,返回當前資源文件的一個惟一標識(由服務器生成),只要資源有變化,Etag就會從新生成。

  • 瀏覽器在下一次加載資源向服務器發送請求時,會將上一次返回的Etag值放到request header裏的If-None-Match裏

  • 服務器只須要比較客戶端傳來的If-None-Match跟本身服務器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。

  • 若是服務器發現ETag匹配不上,那麼直接以常規GET 200回包形式新的資源(固然也包括了新的ETag)發給客戶端;若是ETag是一致的,則直接返回304知會客戶端直接使用本地緩存便可。

image.png

Last-Modified 與 ETag 是能夠一塊兒使用的,服務器會優先驗證 ETag,一致的狀況下,纔會繼續比對 Last-Modified,最後才決定是否返回 304

  • 首先在精確度上,Etag要優於Last-Modified。Last-Modified的時間單位是秒,若是某個文件在1秒內改變了屢次,那麼他們的Last-Modified其實並無體現出來修改.

  • 可是Etag每次都會改變確保了精度;若是是負載均衡的服務器,各個服務器生成的Last-Modified也有可能不一致。

  • 第二在性能上,Etag要遜於Last-Modified,畢竟Last-Modified只須要記錄時間,而Etag須要服務器經過算法來計算出一個hash值。

  • 第三在優先級上,服務器校驗優先考慮Etag

總結

當瀏覽器再次訪問一個已經訪問過的資源時,它會這樣作:

1.看看是否命中強緩存,若是命中,就直接使用緩存了。

2.若是沒有命中強緩存,就發請求到服務器檢查是否命中協商緩存。

3.若是命中協商緩存,服務器會返回 304 告訴瀏覽器使用本地緩存。

4.不然,返回200、最新的資源、緩存標識。

image.png

用戶行爲對瀏覽器緩存的影響

  • 打開網頁,地址欄輸入地址: 查找 disk cache 中是否有匹配。若有則使用;如沒有則發送網絡請求。

  • 普通刷新 (F5):由於 TAB 並無關閉,所以 memory cache 是可用的,會被優先使用(若是匹配的話)。其次纔是 disk cache。

  • 強制刷新 (Ctrl + F5):瀏覽器不使用緩存,所以發送的請求頭部均帶有 Cache-control:no-cache(爲了兼容,還帶了 Pragma:no-cache),服務器直接返回 200 和最新內容。

實踐

強緩存 Cache-Control

image.png

index.js

const Koa = require('koa')
const path = require('path')
//靜態資源中間件
const static = require('koa-static')

const app = new Koa()

// 靜態資源目錄對於相對入口文件index.js的路徑
const staticPath = './static'



app.use( async ( ctx, next ) => {
   // 設置響應頭Cache-Control 設置資源有效期爲300秒
   ctx.set({
    'Cache-Control': 'max-age=300'  
  });
  await next();
})

app.use(static(
  path.join( __dirname,  staticPath)
))

app.listen(3000, () => {
  console.log('[demo] static-use-middleware is starting at port 3000')
})
複製代碼

刷新頁響應頭的Cache-Control變成了max-age=300。

驗證三級緩存原理

進行網絡請求後瀏覽器把圖片存進了磁盤和內存中。 根據三級緩存原理,咱們會先在內存中找資源,咱們來刷新頁面。能夠發現是從內存緩存中查找返回。

image.png

關閉該頁面,內存中的資源被釋放掉, 可是磁盤中的資源是永久性的,因此還存在。 根據三級緩存原理,若是在內存中沒找到資源,便會去磁盤中尋找!能夠發現是從磁盤緩存中查找返回。

image.png

對資源設置的有效期是300秒,咱們接下來來驗證緩存是否失效。300秒後。。。緩存失效了。

image.png

驗證協商緩存

index.js

const Koa = require('koa')
const path = require('path')
//靜態資源中間件
const static = require('koa-static')

const  conditional = require('koa-conditional-get');
const  etag =  require('koa-etag');

const app = new Koa()

// 靜態資源目錄對於相對入口文件index.js的路徑
const staticPath = './static'

app.use(conditional());
app.use(etag());

// app.use( async ( ctx, next ) => {
//    // 設置響應頭Cache-Control 設置資源有效期爲300秒
//    ctx.set({
//     'Cache-Control': 'max-age=300'  
//   });
//   await next();
// })

app.use(static(
 path.join( __dirname,  staticPath)
))

app.listen(3000, () => {
 console.log('[demo] static-use-middleware is starting at port 3000')
})


複製代碼

第一次請求.

image.png 咱們發現返回值裏面已經有了Etag值。

二次請求

瀏覽器帶上If-None-Match請求頭,並賦值爲上一次返回頭的Etag值,而後與 此次返回值的Etag值進行對比。若是一致則命中協商緩存。返回304 Not Modified。能夠看到是命中的。

image.png

協商緩存失效

修改圖片內容看看

image.png 首先咱們二次刷新頁面加載資源向服務器發送請求時,會將上一次返回的Etag值放到request header裏的If-None-Match裏。 Etag是服務器響應請求時,返回當前資源文件的一個惟一標識(由服務器生成),能夠看到圖片變化後,Etag從新生成了

如今能夠明顯看到 客戶端瀏覽器請求頭攜帶的If-None-Match跟服務器上該資源的ETag不一致,ETag匹配不上,以常規GET 200回包形式新的資源(固然也包括了新的ETag)發給客戶端。

大功告成

github實踐代碼地址:github.com/LHDIYU/Koa/…

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息