深刻理解HTTP緩存機制及原理

1、前言

       上週阿里的面試官問了個面試題 「 能不能說下 304 的過程,以及影響緩存的頭部屬性有哪些?」OMG.......由於以前只是大概瞭解 304 狀態碼是表示緩存,且由於平時項目開發過程當中也沒有在緩存這塊踩過坑,因此這一塊也沒有去作特別深刻的研究。因此當被問這個問題時,有被當頭一棒的感受,也好好反思了下,本身校招以軟件開發工程師的職位進入公司,以前沒有想過要從事前端開發,因此前端基礎幾乎能夠忽略不計。工做一年多來,爲在工做上表現突出,在工做上投入大量的精力,幹到晚上10點是常規操做,週末至少加班1天;而且空餘時間,常常看前端相關書籍彌補基礎,如《JavaScript 高級程序設計》、《CSS 權威指南》、《Sass 實踐》、《JavaScript 高性能編程》....《Webpack 實踐》、《深刻淺出 node.js》等不下10本書;在廣度上涉及的仍是不少的,可是存在問題:從事前端時間不長,前端知識雜而多,若是沒有專門準備,若是忽然問你一個知識點,你雖然大概知道這是啥,可是讓你講的話,你很難有條理的講清楚。
       So,下一階段的首要任務:「打好打牢前端基礎,深刻了解所用的技術棧原理」。廢話少說,學問學問,不懂就弄清楚!如下「 理論知識 + 實踐操做」來完全弄懂 HTTP 緩存機制及原理!

2、緩存規則及解析

       爲方便你們理解,我假設覽器存在一個緩存數據庫,用於存儲緩存信息。在客戶端第一次請求數據時,此時緩存數據庫中沒有對應的緩存數據,須要請求服務器,服務器返回後,將數據存儲至緩存數據庫中。以下流程圖所示:
       根據是否須要從新向服務器發起請求來分類,將HTTP緩存規則分爲兩大類( 強制緩存對比緩存)在詳細介紹這兩種規則以前,先經過時序圖的方式,讓你們對這兩種規則有個簡單瞭解。
(1)已存在緩存數據時,僅基於 強制緩存,請求數據的流程以下所示:


(2)已存在緩存數據時,僅基於 對比緩存,請求數據的流程以下所示:


       咱們能夠看到兩類緩存規則的不一樣, 強制緩存若是生效,不須要再和服務器發生交互,而 對比緩存不論是否生效,都須要與服務端發生交互。
       兩類緩存規則能夠同時存在, 強制緩存優先級高於 對比緩存,也就是說,當執行 強制緩存的規則時,若是緩存生效,直接使用緩存,再也不執行 對比緩存規則。

3、緩存經常使用字段

一、http1.0時期的緩存方案


注意: 前端

(1)若是使用了Pragma: 'no-cache'的話,再設置Expires或者Cache-Control,就沒有用了,說明Pragma的權值比後二者高。node

 (2)若是設置了Expires以後,客戶端在須要請求數據的時候,首先會對比當前系統時間和這個Expires時間,若是沒有超過Expires時間,則直接讀取本地磁盤中的緩存數據,不發送請求。
ios

二、http1.1 時期的緩存方案

2.一、Cache-Control 字段 面試

2.1.一、Cache-Control 做爲請求頭字段
數據庫


(1)Cache-Control: no-cache 編程

使用no-cache指令的目的是爲了防止從緩存中返回過時的資源。 客戶端發送的請求中若是包含 no-cache 指令,則表示客戶端將不會接收緩存的資源。每次請求都是從服務器獲取資源,返回304。  axios

(2)Cache-Control: no-store 瀏覽器

使用no-store 指令表示請求的資源不會被緩存,下次任何其它請求獲取該資源,仍是會從服務器獲取,返回 200,即資源自己。
緩存

2.1.二、Cache-Control 做爲響應頭字段
服務器


Cache-Control: public 

當指定使用 public 指令時,則明確代表其餘用戶也可利用緩存。

 Cache-Control: private

當指定 private 指令後,響應只以特定的用戶做爲對象,這與 public 指令的行爲相反。 緩存服務器會對該特定用戶提供資源緩存的服務,對於其餘用戶發送 過來的請求,代理服務器則不會返回緩存。

 Cache-Control: no-cache 

若是服務器返回的響應中包含 no-cache 指令,每次客戶端請求,必需先向服務器確認其有效性,若是資源沒有更改,則返回304. 

Cache-Control: no-store 

不對響應的資源進行緩存,即用戶下次請求仍是返回 200,返回資源自己。 

Cache-Control: max-age=604800(單位:秒) 

資源緩存在本地瀏覽器的時間,若是超過該時間,則從新向服務器獲取。

2.二、請求頭部字段 & 響應頭部字段

2.2.一、請求頭部字段


2.2.二、響應頭部字段


注意:

 (1)If-None-Match的優先級比If-Modified-Since高,因此二者同時存在時,聽從前者。

4、實驗驗證

一、實驗1 — 請求的資源沒修改,驗證2種緩存出現的情形
服務端使用 node.js , 客戶端使用 axios 進行請求:
1.一、請求頭部 / 響應頭部 設置
(1)服務端響應頭部設置:
     res.setHeader('Cache-Control', 'public, max-age=10');
(2)客戶端請求頭部使用默認設置

1.二、實驗步驟
(1)請求 3 次,第一次請求請求資源;第二次在10秒內再次請求該資源,第三次在 10 秒後再次請求該資源(實驗過程當中,服務端的資源沒有進行改變)

1.三、實驗結果
      3 次請求的 HTTP 信息以下圖所示,從圖中的信息能夠得出,第一次請求該資源是從服務器獲取;第二次(10 秒內)請求該資源是直接從瀏覽器緩存中獲取該資源(沒有向服務器確認);第三次(10 秒後)請求該資源時,由於資源緩存時間(10 秒)過時,因此向服務器獲取資源,服務器判斷該資源與本地緩存的資源沒有作更改,因此返回 304,讓客戶端直接從瀏覽器緩存中獲取該資源;如下,根據 HTTP 頭部信息詳細介紹三個操做的交互過程。


1.3.一、第一次請求資源
第一次請求資源的請求頭部和響應頭部的截圖以下所示,由於第一次請求該資源,本地並無緩存,因此直接從服務器獲取該資源;咱們從截圖能夠看到,服務器返回改資源的響應頭部中包含3個屬性與資源緩存相關:
(1) cache-control: public, max-age=10
緩存規則的設置,咱們這個示例中,設置緩存規則爲 public, 而且設置緩存過時時間爲10秒;
(2) etag: W/"95f15b-16994d7ebf6"
資源的惟一標識符,客戶端下次訪問該資源時,會在請求頭中攜帶 etag 去向服務器確認,該資源是否被修改;
(3 )last-modified: Tue, 19 Mar 2019 07:26:12 GMT
資源最後一次修改時間,客戶端下次訪問該資源時,會在請求頭中攜帶該信息去向服務器進行匹配,該資源是否被修改;


1.3.二、第二次請求資源(10秒內,即在緩存時間失效前)
       第二次請求資源的請求頭部和響應頭部的截圖以下所示,由於第二次請求該資源,該資源本地緩存還沒失效,因此就直接從瀏覽器緩存中獲取該資源。


1.3.三、第三次請求資源(10秒後,即在緩存時間失效後)
       第三次請求資源的請求頭部和響應頭部的截圖以下所示,由於第三次請求該資源,該資源本地緩存已經失效,因此在請求頭部中加入 If-Modified-Since If-None-Match 屬性,來向服務器進行確認該資源是否有被更改。



二、實驗2 — 請求的資源進行修改,驗證2種緩存出現的情形
服務端使用 node.js , 客戶端使用 axios 進行請求:
2.一、請求頭部 / 響應頭部 設置
(1)服務端響應頭部設置:
     res.setHeader('Cache-Control', 'public, max-age=20');
(2)客戶端請求頭部使用默認設置

2.二、實驗步驟
(1)請求 3 次,第一次請求資源;而後在服務器對請求的資源進行修改,第二次在 20 秒內再次請求該資源,第三次在 20 秒後再次請求該資源

2.三、實驗結果
       3 次請求的 HTTP 信息以下圖所示,從圖中的信息能夠得出,第一次請求該資源是從服務器獲取;第二次(20 秒內)請求該資源是直接從瀏覽器緩存中獲取該資源(沒有向服務器確認);第三次(20 秒後)請求該資源時,由於資源緩存時間(20 秒)過時,因此向服務器獲取資源,服務器判斷該資源與本地緩存的資源不一樣,因此從新返回該資源;如下,根據 HTTP 頭部信息詳細介紹三個操做的交互過程。


2.3.一、第一次請求資源
       第一次請求資源的請求頭部和響應頭部的截圖以下所示,具體詳細信息與 1.3.1 小節相同,在此不一樣重複介紹。


2.3.二、第二次請求資源(20 秒內,即在緩存時間失效前)
      第二次請求資源的請求頭部和響應頭部的截圖以下所示,(注意:即便此時服務器上的資源已經更改,可是因爲緩存在瀏覽器中的資源沒有過時,因此仍是從緩存中返回舊資源)。


2.3.三、第三次請求資源(20 秒後,即在緩存時間失效後)
       第三次請求資源的請求頭部和響應頭部的截圖以下所示,由於第三次請求該資源,該資源本地緩存已經失效,因此在請求頭部中加入 If-Modified-Since If-None-Match 屬性,來向服務器進行確認該資源是否有被更改,從下圖中能夠看到,響應頭部的屬性 etag 與 請求頭部的屬性 If-None-Match 不一樣,響應頭部的屬性 If-Modified-Since 與 請求頭部的屬性 last-modified 不一樣;因此服務器返回該資源的最新資源。


5、總結

一、對於強制緩存,服務器通知瀏覽器一個緩存時間,在緩存時間內,下次請求,直接用緩存,不在時間內,執行比較緩存策略。
二、對於比較緩存,將緩存信息中的Etag和Last-Modified經過請求發送給服務器,由服務器校驗,返回304狀態碼時,瀏覽器直接使用緩存。
總結流程圖以下所示:


有任何問題歡迎留言討論,若是以爲對你有幫助,請點贊鼓勵~

相關文章
相關標籤/搜索