即時更新 (Real-time Updates)
核心問題
標準 HTTP 是請求-回應模型:client 要、server 回、連線關閉。但即時應用(Google Docs、聊天)需要 server 主動推送給 client,且不能讓每個 client 每幾毫秒輪詢一次(會打垮基礎設施)。
- 第一個 Hop:如何把更新從 server 送達 client?(通訊協定)
- 第二個 Hop:如何把更新從 事件來源傳播到 server?(觸發機制)
第一個 Hop:Client-Server 通訊協定
| 協定 | 方向 | 底層 | 延遲 | 何時用 |
|---|---|---|---|---|
| Simple Polling | client 拉 | HTTP | 高(間隔) | 不在意延遲的基準方案 |
| Long Polling | client 拉(掛起) | HTTP | 中 | 近即時、低頻、偏好簡單 |
| SSE | server → client | HTTP(chunked) | 低 | 單向推送(AI token 串流、儀表板) |
| WebSocket | 雙向 | TCP(HTTP upgrade) | 最低 | 高頻雙向(聊天、遊戲) |
| WebRTC | P2P | UDP | 最低 | 音視訊通話、P2P 資料 |
不在意延遲 → Simple Polling(最簡單基準)
不需雙向 → SSE(輕量,適合大多數推送)
需要高頻雙向 → WebSocket(複雜但效能最強)
需要音視訊通話 → WebRTC(唯一適合)
寫入永遠可以另發 HTTP 請求 —— 常見模式是 SSE 訂閱更新 + 一般 HTTP POST 寫入。除非確實需要雙向,先考慮 SSE。詳見 01-Networking/05-Realtime-Protocols。
各協定要點
- Long Polling:client 發請求,server 掛著直到有資料才回 → 收到後立刻再發。高頻更新時延遲累積(第二個更新可能 290ms 才到)。注意 timeout:LB 別在 30 秒掛斷,但 server 願等 60 秒。
- SSE:用
Transfer-Encoding: chunked把回應變資料流,瀏覽器原生EventSource支援、自動重連。有 last event ID 機制補發中斷期間遺漏的事件。缺點:單向、部分 proxy 不支援串流。 - WebSocket:透過 HTTP「升級」協定建立,可複用 cookie/header。持久有狀態連線帶來麻煩 → 常把 WebSocket 在早期終止到專門的 WebSocket 服務,讓其他系統保持無狀態。
- WebRTC:client 透過**訊號伺服器(signaling server)**互相發現,再直接 P2P。NAT 問題用 STUN(打洞)/ TURN(中繼 fallback)。訊號伺服器本身仍需 WebSocket/SSE。
L7 LB 終止連線再建新連線,破壞 WebSocket 持久連線 → WebSocket 必須用 L4 LB(同一條 TCP)。Long polling 等 HTTP 方案 L7 更靈活。詳見 01-Networking/06-Load-Balancing。
第二個 Hop:Server 端更新傳播
系統不斷產生需推送的更新(他人的編輯、司機位置、聊天訊息)。三種觸發模式:
1. 拉取:Simple Polling
client 不斷向 server 請求,server 用 DB 維護狀態回應。美妙在解耦(DB 橫亙在生產者/消費者之間),代價是失去即時性。
一百萬 client 每 10 秒輪詢一次 = 每秒 10 萬次讀取。
2. 推送:Consistent Hashing
持久連線需要 client 和某台 server 保持連線 → 要知道「用戶 C 連到哪台 server」。
- 簡單雜湊(
user_id % N):N 固定時可行,但增減 server 時幾乎所有用戶要斷線重連 - Consistent Hashing:server 和用戶映射到 hash ring,擴縮容只影響受影響段落 → 連線動盪最小
用 ZooKeeper / etcd 管理 server 分配。適合需維護大量關聯狀態的持久連線(如 Google Docs,連線和特定文件關聯)。詳見 02-Distributed-Systems/04-Consistent-Hashing。
3. 推送:Pub/Sub(廣播首選)
專門服務(Kafka / Redis)收集來源更新,廣播給訂閱者。持久連線連到輕量「端點伺服器」,後者訂閱相關 topic 並轉發。
更新來源 ──► Pub/Sub (topic) ──廣播──► 端點伺服器們 ──► 各自的 client 連線
- 端點伺服器負載平衡容易(用 least connections 即可,不需連特定 server)
- 適合廣播給大量 client、不需維護大量關聯狀態
- 缺點:Pub/Sub 是 SPOF/瓶頸(用 Redis Cluster 分片擴展)、多一層間接
連線需維護大量狀態(協作編輯)→ Consistent Hashing;只傳小訊息、無太多關聯狀態(聊天通知)→ Pub/Sub。
常見 Deep Dive
網路不可靠。WebSocket 不一定發斷線訊號 → 用心跳(heartbeat)偵測「殭屍連線」。恢復時需追蹤 client 已收到哪些訊息:維護按用戶的訊息 queue 或序列號,重連時帶上最後收到的 ID 補發遺漏。Redis Stream 是熱門方案。
經典 fan-out 問題。不是寫入幾百萬個別 feed,而是只快取一次,透過多層分發 —— 各地區 server 拉取更新推給本地 client。詳見 10-Design-Patterns/02-Scaling-Writes 的階層式聚合。
兩訊息走不同路徑可能亂序。Vector clock / 邏輯時間戳建立順序關係。嚴格順序需求 → 讓所有相關訊息流經同一 server / partition(犧牲部分擴展換一致)。產品導向面試中,單一主機/partition + 正確時間戳建立全序最實際;vector clock 更多出現在基礎設施深度題。
常見系統範例
| 系統 | 協定 + 傳播 |
|---|---|
| 聊天 | WebSocket(雙向)+ Pub/Sub 分發;考慮排序、typing indicator、presence |
| 直播留言 | 極端 fan-out → 階層式聚合 + 批次處理 |
| 協作編輯 | WebSocket + OT / CRDT 處理衝突;游標/選取高亮增加複雜度 |
| 即時儀表板 | SSE(單向)+ 注意聚合間隔 |
| 多人遊戲 | WebRTC(P2P 降延遲)+ WebSocket(server 協調) |
何時不用
不在意延遲時,輪詢是極佳基準,複雜度最低(Senior+ 面試非常欣賞)。用輪詢可同時迴避兩個 hop。大多數候選人過度設計即時方案。
Related Notes
- 01-Networking/05-Realtime-Protocols — SSE / WebSocket / WebRTC 協定細節
- 01-Networking/06-Load-Balancing — L4 vs L7、WebSocket 為何用 L4
- 02-Distributed-Systems/04-Consistent-Hashing — 連線路由、ZooKeeper/etcd
- 10-Design-Patterns/02-Scaling-Writes — 名人 fan-out、階層式聚合
- 01-Networking/01-Network-Layers-and-Requests — OSI 分層、TCP 握手、HTTP 旅程
- 10-Design-Patterns/Practice-Design-Patterns