關於 http cache 的一個小問題以及引發的思考

shanyue發表於2019-03-15

關於 http cache 的一個小問題以及引發的思考

本文主要介紹在沒有設定強制快取時的 lm factor 演算法,以及關於快取策略的思考。以及如何使用 service worker 增強快取。

本文地址 shanyue.tech/post/a-prob…

問題回顧

前幾天,寫了一篇部落格,瀏覽器中的二進位制,其中總結了一張圖。

前端中二進位制的轉換

後來,我對圖片做了一些更改,又釋出了上去。這時候問題出現了,圖片沒有更新!

比沒有快取更嚴重的問題是快取了不該快取的東西!

我研究了下該圖片的 Request/Response 資訊,總結如下

  1. 圖片從快取中取,沒有走網路流量,顯示 from memory cache
  2. 響應頭設定了 ETag
  3. 響應頭沒有設定 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 做快取增強。

  1. 使用 workbox 自動生成 sw.js
  2. sw.js 對所有資源打一個 hash 戳,維護一個檔案與hash的鍵值對清單,並使用 Cache API 對所有資源做永久快取。
  3. sw.js 每次請求需要校驗新鮮度
  4. 當靜態資源有所更改,sw.js 維護的鍵值對清單發生變化,sw.js 會獲取到新的資源

此時,只需要 sw.js 每次校驗新鮮度,而無需一大堆檔案都去校驗新鮮度了

相關文章