關於 http cache 的一個小問題以及引發的思考
本文主要介紹在沒有設定強制快取時的 lm factor 演算法,以及關於快取策略的思考。以及如何使用 service worker 增強快取。
問題回顧
前幾天,寫了一篇部落格,瀏覽器中的二進位制,其中總結了一張圖。
後來,我對圖片做了一些更改,又釋出了上去。這時候問題出現了,圖片沒有更新!
比沒有快取更嚴重的問題是快取了不該快取的東西!
我研究了下該圖片的 Request/Response 資訊,總結如下
- 圖片從快取中取,沒有走網路流量,顯示 from memory cache
- 響應頭設定了 ETag
- 響應頭沒有設定 Cache-Control,以及 Expires
現在再來看文章開頭那張圖,如果這是一段面試題,第二次請求圖片資源應該是哪種快取策略
LM factor 演算法
再丟擲一個問題,如何得出某資源的最後更新時間以及本次請求資源所生成的時間
先熟悉以下兩個 Response Header
- Date
- Last-Modified
從這兩個頭可以計算出資源已經多久沒有更新了。
LM factor 演算法在沒有 Cache-Control 以及 Expires 的時候,用來計算應該強制快取多長時間。
演算法大致介紹如下,如果本次請求資源,發現沒有關於強制快取的配置,而且該資源最後一次修改是在 10 小時以前,那麼就對它設定 10 * factor 個小時的快取。factor 即 LM factor,設定為 (0, 1)。
問題總結
如果你不設定 Cache-Control 的話,那你的資源很危險,使用者可能正在訪問已過期的資源!
另外,在對你的應用進行二次重新整理時,你大部分資源都進了快取,載入速度很快。先不要高興太早,有可能不是你快取設定得好,更有可能是你壓根就沒設定快取。
一言蔽之,無論如何,要主動設定 Cache-Control,不要讓瀏覽器替你做決策
問題解決
找到了問題所在,只需新增一個響應頭 Cache-Control: no-cache;
就可以解決問題。
no-cache 代表需要每次校驗資源的新鮮度,來決定是否從快取中取 no-store 代表從不存快取
由於部落格沒有能做長期快取的資源,統一對部落格的所有請求新增了響應頭 Cache-Control: no-cache
。我使用了 Traefik
作為反向代理,修改 docker-compose.yml 如下
version: "3"
services:
blog:
build:
context: .
restart: always
labels:
- "traefik.frontend.rule=Host:blog.xiange.tech"
- "traefik.frontend.headers.customResponseHeaders=Cache-Control:no-cache"
複製程式碼
再次部署後,圖片快取的問題已經解決。
快取策略設定
這時再思考一個專案的快取策略設定
圖片總結如下,參考谷歌開發者文件 developers.google.com/web/fundame…
而我的快取策略簡單總結如下
- 對於帶指紋資訊的資源設定永久快取
- 對於不帶指紋資訊的資源設定 ETag 每次校驗新鮮度
使用 Service Worker 增強快取
如果 304 過多怎麼辦
如果不帶指紋的資源過多,又需要資源保障實時的新鮮度如何處理。這麼一大堆資源每次去向伺服器比對 ETag,伺服器也是很煩的,畢竟也會消耗一些 CPU。
這時候可以考慮使用 service worker 做快取增強。
- 使用 workbox 自動生成 sw.js
- sw.js 對所有資源打一個 hash 戳,維護一個檔案與hash的鍵值對清單,並使用 Cache API 對所有資源做永久快取。
- sw.js 每次請求需要校驗新鮮度
- 當靜態資源有所更改,sw.js 維護的鍵值對清單發生變化,sw.js 會獲取到新的資源
此時,只需要 sw.js 每次校驗新鮮度,而無需一大堆檔案都去校驗新鮮度了