2.3 快取與 CDN:內容傳遞網路的原理#
核心矛盾:光速是天花板#
伺服器在美國,使用者在台北,單程 RTT 至少 150ms。TCP 握手 + TLS 就要耗掉 3 RTT(1.3、2.2 節)—— 物理距離無法壓縮,只能縮短。
\[ T_{\text{載入}} \ge N \times RTT \quad\Rightarrow\quad \text{兩條路:減少往返次數 } N, \text{或縮短 } RTT \]
CDN(Content Delivery Network,1998 年 Akamai 創立)同時做兩件事:把內容搬到離使用者近的地方(縮短 RTT),以及盡量不回源站(減少往返)。
快取的理論基礎#
局部性原理#
快取有效的前提是存取具有局部性:
- 時間局部性:剛用過的資料很快會再用(熱門新聞)
- 空間局部性:用過 A 的人很快會用相鄰的 B(同一頁的資源)
Web 流量高度符合冪律分布:少數熱門內容承載多數流量 —— 這正是快取的甜蜜點。
HTTP 快取控制#
Cache-Control: max-age=31536000, immutable # 帶 hash 的靜態資源:快一年
Cache-Control: no-cache # 可快取但每次需重新驗證
Cache-Control: private, max-age=0 # 個人化資料,只快在瀏覽器
ETag: "a3f-5c2e" # 內容指紋
條件請求(節省頻寬):
sequenceDiagram
participant B as 瀏覽器
participant S as 伺服器
B->>S: GET /app.js (If-None-Match: "a3f-5c2e")
S-->>B: 304 Not Modified(無 body!)
Note over B: 使用本地快取304 的威力:省下整個 body 的傳輸 —— 只傳元資料做一致性確認。
CDN 的架構#
flowchart TB
U1[台北使用者] --> Edge1[邊緣節點 台北]
U2[柏林使用者] --> Edge2[邊緣節點 柏林]
Edge1 -->|未命中| Mid[中層節點]
Edge2 -->|未命中| Mid
Mid -->|未命中| Origin[源站<br/>你的伺服器]回源與命中率#
\[ \text{命中率} = \frac{\text{邊緣命中}}{\text{總請求}} \quad\quad \text{源站負載} = (1 - \text{命中率}) \times \text{總流量} \]
命中率 95% 意味著源站壓力降到 1/20 —— CDN 本質上是用快取置換算力與頻寬成本。
邊緣節點怎麼選?DNS 的二次就業#
使用者請求 cdn.example.com 時,CDN 的智慧 DNS 依查詢來源 IP 判斷地理位置,回應最近邊緣節點的 IP(1.4 節地理路由)—— DNS 從「名字解析」升級為「流量調度器」。
靜態與動態內容的分界#
| 內容 | 策略 |
|---|---|
| 圖片、字型、帶 hash 的 JS/CSS | Cache-Control: max-age=31536000, immutable |
| HTML | no-cache(每次驗證)或短 max-age |
| API 回應 | 視資料性質;個人化資料不可共享快取 |
| 動態請求 | 不快取,但 CDN 仍提供 TLS 終結、連線最佳化 |
快取失效(cache busting)的標準解:檔名帶內容雜湊 app.a3f5c2e.js,內容一改檔名就變 —— 以命名策略繞過失效難題,而不是去「清除」快取。這是前端建置工具(webpack/Vite)的檔名雜湊功能的由來。
「開發快取」與「程式快取」之外: Service Worker#
// 註冊 Service Worker:把快取控制權交給 JavaScript
navigator.serviceWorker.register('/sw.js');
// sw.js
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) =>
cached ?? fetch(event.request).then((res) => {
const copy = res.clone();
caches.open('v1').then((c) => c.put(event.request, copy));
return res;
})
)
);
});
Service Worker(2014)把快取策略變成可程式化的中介層(cache-first、network-first、stale-while-revalidate),也是 PWA 離線能力的基礎。
習題#
- 用「減少 N 或縮短 RTT」框架,解釋 CDN 對兩者的貢獻。
- 為什麼 HTML 用
no-cache而帶 hash 的 JS 用immutable?如果 HTML 也快一年會發生什麼? - 實作一個 stale-while-revalidate 策略的 Service Worker 片段,說明其取捨。