為什麼要使用 HTTP 緩存
- 對於資源:
假如每次請求的請求頭和響應頭分別都是1k,請求文件大小是10k。那麼一次請求就是12k大小,n次就是(12 * n)k。
- 對於前端:
前端每次請求完畢都要重新渲染 影響用户體驗
- 對於後端:
沒有緩存機制的話前端會頻繁的請求後端的接口。後端每次都要提供查找和下載等功能。如果請求基數比較大,服務器就會存在較大的壓力。
為了減少網絡帶寬消耗、減少延遲與網絡阻塞,同時降低服務器壓力,提高服務器性能。
HTTP 緩存的內容是什麼
CSSJS圖片等 更新頻率不大的靜態文件等
HTTP 緩存頭部字段
- Cache-Control
存在於請求頭和響應頭中,是緩存控制字段
控制HTTP緩存的最高指令,緩存不緩存 它説了算
- 相關value
no-store: 不緩存
no-cache: 緩存 但是前端使用緩存前 都會請求服務器 來判斷當前的緩存資源是否是最新 只是用不過期的緩存
max-age=x(單位秒):請求緩存後的x秒內 不再發起請求,HTTP1.1以上
s-maxage=x(單位秒):代理服務器請求源站後x秒內不再發起請求,只對CDN有效
public:客户端和代理服務器(CDN)都可以緩存
private:只有客户端可以緩存
Expires
- 響應頭 代表資源過期時間,由服務器返回提供。但是瀏覽器端可以修改
Expires。它是HTTP1.0的屬性,在與max-age共存的情況下,max-age的優先級較高(因為max-age是HTTP1.1的屬性) - 場景:
- 瀏覽器向服務器請求了一個
a.js。- 服務器説:‘你煩不煩? 我們約定個時間
Expires,時間還沒到就別來煩我了’。- 然後服務器就返回了
a.js和過期時間Expires。- 後續瀏覽器的請求 會先比對當前時間是否以及大於了Expires過去時間。
如果還沒過期 就不會發起請求 使用緩存,如果過期了 就會重新發起請求。
- 缺點:
可能時間過期了,但是重新發起請求後 發現當前資源a.js並沒有發現變化。這樣就造成了請求的浪費
- 進階:
使用Last-Modified和If-Modified-Since
使服務器和瀏覽器之間在Expires的基礎上,增加一個羣文件最新修改時間來輔助判斷是否應該使用緩存
Last-Modified / if-Modified-Since
- Last-Modified:響應頭 資源最新修改時間
由服務器告訴瀏覽器 - if-Modified-Since:請求頭 資源最新修改時間
由瀏覽器告訴服務器。和Last-Modified是成對出現的,它們兩個會共同對比來決定這個文件要不要重新發送 - 場景:
- 瀏覽器向服務器請求了一個
a.js。- 服務器説:’你煩不煩? 我們約定個時間
Expires,另外再給你一個文件最新修改時間Last-Modified,到時候時間到期了,我們就比對文件最新修改時間,對得上你就繼續使用緩存‘。- 然後服務器就返回了
a.js和過期時間Expires和文件最新修改時間Last-Modified。- 後續瀏覽器請求會先對比是否超過了
Expires過期時間。沒過期就不發起請求,僅使用緩存。如果過期了 瀏覽器在後續請求服務器時就會帶上文件最新修改時間If-Modified-Since。- 服務器收到該請求後 會將
Last-Modified 和 if-Modified-Since進行比對。- 如果
Last-Modified 和 if-Modified-Since不一樣,服務器就會去查找最新的a.js,同時會再次返回最新的a.js和Expires和Last-Modified。- 如果
Last-Modified 和 if-Modified-Since一樣。服務器就會返回304 - Not-Modified,表示之前的緩存還可以繼續使用。
- 缺點:
Expires不太穩定,瀏覽器端可以隨意的修改Expires。
Last-Modified只能精確到秒。在極端情況下,假設文件在1s內發生了變動,那麼此時Last-Modified就無法感知到該文件的變化,這樣瀏覽器永遠都拿不到最新的文件資源。
- 進階:
讓服務器和瀏覽器在過期時間Expiress+ 最新修改時間Last-Modified的基礎上,增加一個文件內容唯一對比標記Etag和if-None-Match。而Expires不太穩定 再加入一個max-age來加以代替。
Etag / if-None-Match
- Etag:響應頭,資源標識,由服務器告訴瀏覽器
- if-None-Match:請求頭,資源緩存標識,由瀏覽器告訴服務器,其實就是
上次服務器給瀏覽器的 Etag。和 Etag 是一對的,它們會進行對比(用法比 if-Modified-Since 更高級一些) - 場景:
- 瀏覽器向服務器發起了
a.js的請求- 服務器説:'你煩不煩? 我們約定個時間
Expires,再給你一個max-age=60(秒),Last-Modified也給你,另外再給你一個文件內容唯一標識符Etag'。- 然後服務器就返回了
a.js和過期時間Expires和max-age=60,和文件最新修改時間Last-Modified和文件內容唯一標識符Etag。- 後續瀏覽器在
max-age=60秒內,就不會再次發起新的請求而是直接使用緩存。並且此時因為有了max-age的存在,Expires已經沒用了。- 後續瀏覽器請求在
max-age=60秒後,會攜帶上If-Modified-Since(服務器發送的Last-Modified) 和if-None-Match(服務器發送的Etag)。- 服務端會比對
if-None-Match和Etag,儘管此時也傳遞了If-Modified-Since,但是服務端不會再對比if-Modified-Since和Last-Modified。因為Etag的優先級大於Last-Modified。Etag更精準的解決了文件資源在1s內的變動問題。7.服務端比對後發現,
if-None-Match 和 Etag不想等。説明a.js被修改過,服務器就會返回最新的a.js和全新的Etag和max-age,也會同時返回Expires和Last-modified,雖然它倆已經沒什麼作用了。
- 服務端經對比發現,
if-None-Match與Etag相等。説明a.js沒有任何變化,返回狀態碼304告訴瀏覽器繼續使用之前的本地緩存
- 缺點:
在Expires和max-age都沒有過期的情況下,瀏覽器是沒有辦法主動知道文件資源是否變動。因為在時間未到的情況下,瀏覽器肯定使用本地的緩存資源。
- 進階:
這種問題在HTTP協議本身上來講,就很難解決了
-
- 通過
md5 / hash 緩存
- 通過
通過不緩存 html,為靜態文件添加 md5 或者 hash 標識,來解決瀏覽器無法跳過緩存過期時間內主動感知文件變化的問題。
只需在項目每次發佈迭代的時候,給靜態文件添加不同的 md5/hash 標識即可。因為文件名並不一樣,服務端會認為是新的文件,所有跟各種緩存字段都沒有任何關係,也就不會存在緩存問題。
-
- 通過
CDN 緩存
- 通過
CDN 是構建在網絡之上的內容分發網絡,依靠部署在各地的邊緣服務器,通過中心平台的負載均衡,內容分發,調度等功能模塊,使用户就近獲取所需內容,降低網絡阻塞,提高用户訪問響應速度和命中率,有一個字段是專門給 CDN 來使用的s-maxage=x(單位秒)
CDN 緩存的工作方式
- 第一次請求
- 瀏覽器向服務器請求
a.js資源。- 服務端:'
a.js這個文件我給我小弟 CDN 了,以後你要這個就找 CDN 吧,別找我了。- 成功返回
a.js給 CDN,CDN 進行緩存。同時CDN 返回給瀏覽器,瀏覽器自己也進行了緩存'。
- 後續請求
- 瀏覽器緩存時間過期,再次發起請求時。這時候 服務器就不會理你了。此時
CDN會幫你查找該資源,同時也分幾種情況:1-1. CDN節點自己緩存的文件還沒有過期,CDN會打回該請求。返回狀態嗎
304,告訴瀏覽器 之前的緩存資源還能使用。
1-2. CDN節點自己緩存的文件已經過期了。為了保險起見,CDN自己會發生請求到源服務器,成功拿回最新數據後,再返回給瀏覽器。