01、什麼是HTTP快取,如何工作的?
當我們開啟一個頁面時,會向服務端發起很多次請求,如下圖開啟百毒首頁,發起了HTML、各種圖片、JS、CSS等資源共72次請求。這裡面很多資源並不會頻繁變化,每次開啟頁面都重新請求下載,就很浪費了。
瀏覽器快取也稱為HTTP快取,HTTP快取簡單理解就是本地(瀏覽器)快取了HTTP響應,以便後續複用,減少向服務端的請求。尤其對於一些靜態資源,如圖片、JS檔案、CSS檔案等資源,瀏覽器端快取可以極大提升頁面的載入速度,不用每次都都全部從服務端下載,也減輕了服務端的壓力。
如下圖,再次開啟百度首頁,可以看到很多圖片、JS資源、CSS資源都是從快取載入的,網頁載入飛快。
?快取是一個很重要、很常用的技術,在IT系統中無處不在,基本原理都是就近儲存可能要用的資料,用空間換時間,提升資料的載入效率。
- 前端瀏覽器快取HTTP的響應。
- 後端服務的資料API也會有快取,避免每次都訪問資料庫,資料庫本身也會有快取。
- CDN內容分發也是一種分散式的快取。
- 計算機中的CPU、磁碟也都會有自己的快取機制。
1.1、基於URL快取資源
URL是定位資源的唯一路徑,通常來說快取也是基於URL地址的。但不僅限於URL,也支援以透過Header的欄位Accept、Accept-Language 、 Accept-Encoding標記的不同內容進行快取,可透過Vary
欄位來申明(/ˈveri/ 改變)。
Vary: Accept-Language
1.2、存在什麼地方?
主要儲存在記憶體、磁碟上,具體存在哪則由瀏覽器來決定了,比如大檔案可能會存在磁碟上,使用很頻繁的資源可能會儲存在記憶體中,或者都會儲存。
- 記憶體
from memory cache
:記憶體讀取速度快,程式結束記憶體就釋放了。 - 磁碟
from disk cache
:讀取速度稍慢(其實也是很快的),可以長久儲存。
1.3、快取規則
基本的快取讀取規則都是先從本地瀏覽器快取查詢,如果有且資源沒有過期就用本地的,否則求去服務端請求。具體過程又分為強快取、協商快取:
- 第一次請求:本地沒有,訪問伺服器獲取響應,瀏覽器會快取響應。
- 第二/N次請求:本地有快取,快取內容有效(沒有過保質期),則響應狀態碼為200,直接使用本地快取。—— 這個過程中,直接使用了本地的快取內容稱為“強快取”。
- 第N次請求:本地有快取,但快取內容已過期,則會向服務端發起請求詢問該資源是否有變化,服務端判斷如果資源沒變化,則返回304(資源沒變化,可繼續食用,沒有body內容),瀏覽器使用本地快取內容(並更新有效期)。如果服務端判斷資源變了,則返回狀態碼200 + 新的資源,瀏覽器收到會更新快取。—— 這個過程中,瀏覽器詢問了服務端資源是否有效,進行了協商,稱為“協商快取”。
所以快取的使用過程就是,先強快取(本地快取有效),再協商快取。
304: Not Modified 資源未修改,客戶端快取了資源,重定向到本地。
02、強快取:本地快取
?強快取:瀏覽器自行決定的快取處理機制,不需連線服務端。瀏覽器在請求資源時,先在瀏覽器快取中查詢,如果找到,且快取有效期正常,則強快取生效,直接從快取中獲取響應。否則就會使用協商快取,向服務端發起請求。
所以這裡關鍵就是判斷快取是否有效,是否在有效期內。關鍵(Header)欄位就是:
Expires
(HTTP/1.0):過期時間。Cache-Control:max-age
(HTTP/1.1):快取有效時長。
?都是服務端響應時,在Header上標記的值,瀏覽器根據這個欄位值來判等資源是否有效。
Cache-Control:max-age
優先順序更高,也更好用。
2.1、Expires過期時間
HTTP/1.0的技術(已過時),在Header中使用,規定快取的具體有效期(年月日時分秒),在此期限內則快取有效。
Expires: Tue, 28 Feb 2022 22:22:22 GMT
缺點是:值的解析和計算不方便,而且依賴於本地的時間,容易被篡改。
2.2、Cache-Control:max-age
Cache-Control 是HTTP/1.1中的一個Header欄位,用來實現快取的各種配置。常用指令max-age
設定快取的最大有效時長,用來替換Expires
,如果同時存在則max-age
優先。
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=3600 //3600秒,表示一個小時內有效
Cache-Control
常用指令:
指令 | 說明 |
---|---|
max-age | 快取最大週期 (單位秒),超過這個時間快取被認為過期 |
no-cache | 強制重新驗證,不會直接使用本地快取,必須每次都詢問伺服器,就是後文的協商快取 |
no-store | 不快取,禁用快取 |
public | 都可以快取的公共資源,客戶端、代理伺服器都可以快取 |
private | 個性化的、私有化內容,每個使用者請求URL相同但的內容不同的資源,只能客戶端快取,代理伺服器不可快取 |
must-revalidate | 過期後必須重新驗證(revalidate /riˈvælɪˌdet/ 重新驗證) |
immutable | 長時間快取(/ɪˈmjuːtəb(ə)l/ 永恆的) |
Cache-Control: private
Cache-Control: public, max-age=31536000 //1年有效期
Cache-Control: max-age=31536000, immutable
Cache-Control: max-age=0, must-revalidate //立即失效,必須重新驗證(協商快取)
如果沒有設定Cache-Control
欄位,採用“啟發式快取“,儘量的最大限度的使用快取機制。
03、協商快取:服務端驗證
?協商快取:瀏覽器需要連線服務端,向服務端詢問資源是否過期,服務端判斷沒過期就繼續用本地快取,否則返回新的資源。
✔️基本過程都是:
- 請求HTTP資源時,先查詢快取,找到了但快取過期,或快取設定了
no-cache
,開始“協商快取”。 - 發起一個HTTP請求,向服務端詢問資源是否過期。
- 服務端收到請求後驗證資源是否變更,若沒變更則返回304(沒有body,所以速度很快),告訴瀏覽器資源沒有修改,可以繼續使用本地快取。否則返回200+新的資源。
✔️協商快取的關鍵(Header)欄位為:
last-modified
:資源最後修改時間。ETag
:資源的唯一編碼,檔案內容變更後更新。
✔️觸發協商快取的條件:
Cache-Control:no-cache
:強制使用協商快取。- 本地快取過期,或
Cache-Control: max-age=0
3.1、last-modified 最後修改時間
last-modified
最後修改時間,服務端在Header上給出的資源最後修改時間。
- 第一次請求資源時,服務端會在響應Header中會返回
last-modified
欄位。
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600 //快取有效期1個小時
- 再次請求該資源時,瀏覽器發現快取資源已過期(時間已超過1個小時),開始“協商快取”。
- 先發起一個(詢問)HTTP請求,Header中加上
if-modified-since
(/ˈmɑːdɪfaɪd/ /sɪns/),值為上次的last-modified
值。
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT
- 服務端收到請求,判斷如果資源最後修改時間相同,認為資源沒有變化,可以繼續使用本地的快取,則返回304(無body內容)。否則返回200+新的資源,並更新
last-modified
。 - 瀏覽器收到304響應後,從本地快取獲取資源,並恢復其正常快取狀態,更新
Last-Modified
,生命延遲1小時。
HTTP/1.1 304 Not Modified
Content-Type: text/html
Date: Tue, 22 Feb 2022 23:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
Cache-Control: max-age=3600
- 如果瀏覽器收到200響應,同第一次請求資源一樣,更新快取資源。
⛔缺點:如果1s內有多次檔案修改,則會判斷出現問題,因此就有了下面的
ETag
。
3.2、ETag 資源唯一標籤
ETag
,機制和last-modified
類似,ETag
是檔案內容的一個唯一識別符號,檔案變更後會更新識別符號,可以看做是檔案內容的摘要。值是服務端自定義生成的,目的是為了標記檔案內容的版本,可以用版本號、雜湊值等。
- 第一次請求資源時,服務端在響應Header中會返回
ETag
欄位。 - 協商快取時傳送HTTP請求,瀏覽器在Header中加上
If-None-Match
,值為上一次的ETag
值。 - 服務端收到請求,讀取
If-None-Match
值,判斷如果匹配一致,檔案沒更新,則返回304。否則返回200+新的資源,並更新ETag
。 - 瀏覽器收到304響應後,從本地快取獲取資源,並恢復其正常快取狀態,更新
ETag
。 - 如果瀏覽器收到200響應,同第一次請求資源一樣,更新快取資源。
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=0
Connection: keep-alive
Content-Length: 114
Content-Type: text/html
Date: Wed, 19 Apr 2023 09:05:30 GMT
Etag: "639b0692-72"
Expires: Wed, 19 Apr 2023 09:05:30 GMT //2023年4月19日09:08,今天早上,現在是下午5點多了。
Last-Modified: Thu, 15 Dec 2022 11:35:46 GMT
?相比較而言,ETag要更科學、更準確一些。檔案修改了並不程式碼內容一定有變化,還有就是最後修改時間的單位是秒,如果1秒內的多次變化就會判斷失誤。Etag 優先順序高於 Last-Modified。
04、總結一下-流程圖
畫圖!
05、一些實踐
5.1、快取應用
- 不經常變的靜態資源,如JS、CSS、圖片推薦使用強制快取,
Cache-Control: max-age=31536000
。 - HTML文件推薦使用協商快取,比如SPA的入口頁面可以使用
Cache-Control: no-cache
強制每次都使用協商快取。 - Last-Modified、ETag可同時使用,ETag優先順序更高,但
Last-Modified
還有額外的價值,可用來標識檔案的修改時間。 - 合理定製不同資原始檔的快取機制、快取週期。
- 快取的Header配置都是在服務端,可以透過Nginx配置。
5.2、快取破壞
快取破壞:每次內容變化時都更新URL地址,一般就是更新檔名,或檔名附加版本號。大多WEB專案的JS、CSS檔案都是這麼幹的,每次編譯的時候都會更新檔名。
# version in filename
bundle.v123.js
# version in query
bundle.js?v=123
# hash in filename
bundle.YsAIAAAA-QG4G6kCMAMBAAAAAAAoK.js
# hash in query
bundle.js?v=YsAIAAAA-QG4G6kCMAMBAAAAAAAoK
5.3、如何清除快取?
- 瀏覽器強制(忽略快取)重新整理:
Shift + F5
,Command + Shift + R
(Mac)。 - 清除瀏覽器快取:設定 > 歷史記錄 > 清除瀏覽資料。
- 瀏覽器禁用快取:瀏覽器除錯模式下 > 網路 > 開啟“禁用快取”。
參考資料
- HTTP 快取(MDN)
©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀