Hero Image
- Mark

即時聊天系統架構:從需求到容量估算的全景筆記

即時聊天系統架構:從需求到容量估算的全景筆記

本文整理自一支系統設計教學影片,加上我自己的筆記與延伸觀點。當作 reference 寫,之後面試或真的要設計即時通訊服務的時候,可以直接回來查。

一、從需求開始:聊天系統到底要解決什麼

聊到聊天系統,第一個反應就是「不就是訊息收發嗎?」但真要展開設計,需求面要先講清楚,否則後面的架構選擇會沒有依據。

Functional Requirements

  • One-on-One Chat:一對一聊天是核心。
  • Online Message Delivery:對方在線時,訊息要即時送到。
  • Offline Message Delivery:對方離線,訊息要存起來等他上線讀。
  • Group Chat:多人聊天,但建議限制人數(微信上限約 200 人,邀請制可到 500 人)。
  • Presence Status:線上狀態顯示,像 QQ 那種頭像亮燈。

Non-Functional Requirements

  • Scalability:假設 10 億使用者註冊(1 Billion Users)。
  • Latency:低於 500ms,最好秒級內。
  • High Consistency:雙方看到的訊息順序要一致;訊息不能丟。
  • Fault Tolerance:不能隨便掛。

Multi-Device 是加分題,不是必答題。影片原話講得很直接:「主要的先解決,剩下的有時間再說。」

二、客戶端與伺服器的三種對話方式

這題我自己在準備面試時,常被問倒。因為一般設計 Twitter、Instagram 是「客戶端主動拉」,但聊天是「雙向推」。伺服器端要主動把訊息丟給客戶端,但網路層的 NAT 讓這件事變得麻煩。

1. Polling(輪詢)

最直覺:客戶端每隔幾秒問伺服器「有新訊息嗎?」

  • 90% 的查詢是空的(浪費資源)
  • 每次查詢都要重新建立 TCP 連線,三次握手成本高
  • 優點:實作簡單,相容性 100%
  • 缺點:伺服器負擔大、延遲高

2. Long Polling(長輪詢)

客戶端掛一個長連線在伺服器上,有訊息就回,沒訊息就 hold 著直到 timeout 再重連。

  • HTTP-based,相容性好
  • 需要管理連線的掛起、超時、喚醒邏輯
  • 比 Polling 節省資源,但伺服器維護大量掛起連線也是成本

3. WebSocket

真正的雙向通訊,伺服器和客戶端都可以主動發訊息。

  • 延遲最低、傳輸效率最高
  • 但需要伺服器和客戶端都支援 WS 協定
  • 舊瀏覽器相容性差

行動端的額外考量

手機 App 才是聊天系統的主戰場。Wi-Fi 切到 4G、基地台換了,IP 就變了,這時候 WebSocket 要重建連線。所以要引入 Client Token 來定位使用者,而不是用 IP。

更進階的選項是 QUIC(Google 提出),原生支援跨網路切換、阻塞控制。問題的時候可以 Fallback 回 WebSocket。

我的取捨建議:如果不是為了面試,現在新專案直接上 WebSocket + Client Token,行動端考慮 QUIC。Long Polling 留給必須相容舊瀏覽器的場景。

三、Session Register:在線狀態與路由基礎

定好通訊方式後,下一個問題是:訊息要送到哪台伺服器?

Heartbeat 機制

使用者登入時,會被分配到一台 Chat Server。客戶端每 10 秒發一次心跳(Heartbeat)。如果伺服器幾分鐘沒收到心跳,就視為離線。

存什麼

用一個 KV Store 存「會話註冊表」(Session Register):

欄位 範例
User ID u_12345
Last Heartbeat 1723891200
Chat Server chat-server-42
Metadata device type, app version

容量估算

  • 每條 Session 記錄約 100 bytes
  • 10 億註冊 × 20% DAU × 25% 在線 = 5000 萬在線
  • 5000 萬 × 100 bytes ≈ 5 GB

一般 KV Store 頂得住。

Presence 通知怎麼推

使用者狀態變化(上下線)時,可以推給他的線上好友。每個使用者在 Chat Server 上有一個「邏輯收件箱」,Presence 通知直接進收件箱。

這類通知對一致性要求不高——你晚幾秒收到「好友上線」的提醒,沒人會在意。

四、訊息投�架構:兩種主要路線

這一段是聊天系統設計的核心。有兩個完全不同的方向:把雙方湊到同一台機器,或讓訊息在機器間跑。

方案 A:Session Affinity(會話親和)

把正在聊天的兩個使用者,盡量遷到同一台 Chat Server。

  • 優點:
    • 訊息本機投遞,延遲最低
    • 訊息排序簡單(用本地時鐘就好)
  • 缺點:
    • 使用者會在伺服器之間不停遷移,造成業務抖動
    • 熱門使用者會造成 Hotspot(rehash、sharding 都得做)

適合:即時性要求極高的場景(例如多人遊戲房間內聊天)。規模小也適用。

方案 B:Message Sync(訊息同步)

每個使用者掛自己的 Chat Server,伺服器之間透過 RPC 或訊息佇列轉發。

  • 優點:
    • 水平擴容容易,使用者多就加機器
    • 使用者不需遷移,直接連自己的 Chat Server
  • 缺點:
    • 多一層轉發,延遲高
    • Session Register 必須精準(錯了就送不到)
    • 雙端時鐘不同步,訊息順序問題複雜

優化:中心化 Pub/Sub Message Bus

如果你有 N 台 Chat Server,伺服器之間全互聯是 N² 條鏈路,太複雜。

更好的做法是引入一個中心化的 Pub/Sub 訊息匯流排:

  1. 訊息先寫到中心訊息佇列
  2. 接收方的 Chat Server 訂閱屬於自己的 topic
  3. 訊息一體化處理:持久化 + 排隊 + 轉發
  • 持久化、訊息排隊、轉發整合在一起
  • 鏈路複雜度降低(從 N² 變成中心 fan-out)
  • Chat Server 可獨立彈性擴容

風險:中心化架構容易出 Hotspot。實務上要做 sharding、多佇列切分。

五、容量估算的具體數字

系統設計面試最容易被問倒的不是架構圖,而是「你的數字哪裡來的?」

訊息量

  • 峰值在線:50M(5 千萬)
  • 高峰時段:每 5 秒一條訊息 = 0.2 msg/sec/user
  • 總訊息量:50M × 0.2 = 每秒 1000 萬條訊息

單訊息大小

欄位 大小
Message ID 8 bytes
From (User ID) 8 bytes
To (User ID) 8 bytes
Content(200-300 漢字 UTF-8) 600-800 bytes
Timestamp 8 bytes
小計 < 1 KB

壓縮後約 0.5 KB/條。

總流量

10M msg/sec × 0.5 KB = 每秒 5 GB。

現代資料中心吃得下。

Chat Server 數量

兩種情境差很多:

  • WhatsApp 等級(單機 200 萬並發):50M ÷ 2M = 25 台就夠
  • 普通機器(單機 5 萬並發):50M ÷ 50K = 1000 台

WhatsApp 2012 年的技術部落格講過,他們單機能撐 200 萬連線,用的是 Erlang + Actor 模型,這是特例不是常態。

地理親和的假設

聊天對象大概率是身邊的人(同事、家人、朋友),地理相近會被分到同一個資料中心。

  • 同機率假設 20%
  • 80% 訊息走跨服轉發

單機轉發量估算:

2M users × 0.2 msg/sec × 0.5 KB × 80% 轉發 = 每台 160 MB/s

加上雙向(1-1 chat 兩邊都要送),約 320 MB/s。

普通機器或 Kafka 都能扛。如果走 Kafka,10 台 broker 處理 5 GB/s 完全沒問題。

六、群聊、離線訊息與隱私

群聊實作

群聊訊息 Schema 和一對一類似:

group_id | message_id | user_id | content | timestamp
約 0.5 KB/條

需要一個 Group Registration,記錄每個群成員分散在哪些 Chat Server。

範例:500 人群分散在 10 台 Chat Server → 訊息要複製 10 份跨服。但伺服器內部可以用同機路由減少跨服開銷。

離線訊息

對方不線上時,訊息進 Offline Storage:

  • 2 億 DAU × 每天 40 條 × 15% 離線 × 0.5 KB = 0.6 TB/day
  • 保留 30 天 ≈ 18 TB

實作選擇:

  • 簡單場景:檔案系統,每個使用者一個資料夾,log file append-only
  • 高吞吐:NoSQL / KV Store,水平擴容友善

離線訊息對延遲不敏感,技術選型可以保守。

隱私 vs 資料安全

影片最後一段我覺得蠻有意思的:

「如果不是很必要,我們應該盡可能少保存聊天資料。一旦 message deliver 後就從記憶體刪掉,既保護使用者,又降低洩漏風險,同時降低系統複雜度。」

企業用的 IM(Slack、Teams)需要長期保存聊天記錄沒問題,但面向一般消費者的產品,這個權衡要重新思考。GDPR、隱私法規、使用者信任——這些都不是技術問題,但會影響你的架構選擇。

結語

聊天系統架構沒有「標準答案」。每一個選擇都是延遲、複雜度、可靠性的三角權衡。

WhatsApp 用 Actor 模型 + Erlang 做到單機 200 萬並發,這是特例不是常態。大部分團隊會落在「中心化訊息佇列 + 多 Chat Server 轉發」的甜蜜點,並靠 sharding 和多佇列切分避免中心瓶頸。

面試時把 trade-off 講清楚,比背架構圖更重要。數字記得有把握(5 GB/s、25 台、200 萬連線),這是基本功。

之後如果要真正實作,重點會落在三件事:

  1. 訊息順序保證(單調遞序 ID vs 邏輯時鐘)
  2. 訊息投遞保證(ack、重試、exactly-once 語意)
  3. 離線訊息同步(增量同步、last-read cursor、conflict resolution)

這幾個題目之後再寫一篇。


參考資料:

  • YouTube 影片:「Design a Chat System」系統設計教學
  • WhatsApp Engineering Blog(2012 單機 200 萬連線案例)
  • C10M Challenge(單機 1000 萬並發連線目標)

    八、架構師怎麼看:即時聊天系統在 2026 年的業界實作

影片把聊天系統拆得很完整——WebSocket、會話親和、Pub/Sub、容量估算。但有幾個地方我做了 5 年後端會想翻轉,還有幾個業界已經不用、影片卻還在用的概念。

1. WebSocket 在 2024 年後已經不是唯一答案

影片說 WebSocket 是「真正的雙向通訊」,2026 年後這句話要修正——SSE (Server-Sent Events) + HTTP/2 在很多場景已經比 WebSocket 更好。

特性 WebSocket SSE + HTTP/2
通訊方向 雙向 單向(server → client)
協定升級 需要 ws:// upgrade 普通 HTTP/2 stream
行動網路相容 容易卡(NAT timeout) HTTP/2 multiplex 沒問題
CDN 支援 需要特殊處理 完全相容
自動重連 要自己寫 瀏覽器原生
訊息順序 自管 HTTP/2 frame ordering 保證

WhatsApp 2023 年開始大規模把 WebSocket 換成 QUIC + HTTP/3 stream。Discord 2024 年的狀態顯示他們的 voice gateway 用的是純 HTTP/2。

架構師判斷:

  • 純文字訊息:SSE + HTTP/2 就好,不需要 WebSocket
  • 需要 client → server 也走同一條連線(如 typing indicator):WebSocket
  • 跨網路切換、低延遲:QUIC(但要客戶端支援)

我自己的專案現在預設都是 SSE first,WebSocket fallback——少很多 deployment 麻煩。

2. Session Affinity 在 K8s 環境做不起來

影片很推 Session Affinity,但 2026 年我必須打臉:

K8s 的本質是 stateless。Pod 會被 evict、會被 autoscaler 砍掉、會被 rolling update 重啟。你做的 Session Affinity 在這些事件發生時就失效——使用者被迫 reconnect 到新 IP,session state 還是要從 Redis 拿回來。

架構師現在的判斷:

  • 不要再做 Session Affinity。讓 client 端 connect 到任意 pod,pod 從 Redis 拿 session state
  • 把「會話親和」的成本(hotspot、migration、state 同步)轉成 Redis 的成本——便宜、簡單、可預測
  • 真正要做 affinity 的場景只有兩個:多人遊戲房間(latency < 50ms)和直播連麥(audio jitter < 10ms)。一般聊天不需要

業界現況:Discord 用 stateless gateway + Redis Cluster,單台 gateway 掛了自動切換,session 從 Redis 撈。WhatsApp 內部也是同樣設計(雖然對外講「Erlang magic」,骨子裡還是分散式 KV)。

3. Pub/Sub Message Bus 選型:Redis Streams 取代 Kafka

影片推 Kafka for chat,但我做了幾個 chat product 後的看法是:Kafka 對聊天 over-spec。

理由:

  • 訊息保留時間:聊天不需要 replay 30 天。Redis Streams 預設保留長度(maxlen)就能 cover
  • 運維成本:Kafka 至少要 3-5 台機器(KRaft mode 最低),Redis Cluster 3 台就夠
  • 延遲:Redis Streams p99 < 5ms,Kafka p99 通常 20-50ms

但 Redis Streams 不是萬靈丹,Kafka 還是贏在兩個地方:

  • 跨資料中心 replication:Kafka MirrorMaker 2 / Cluster Linking 比 Redis Cluster Federation 成熟
  • 每秒訊息 > 100 萬:Kafka partition 可以水平擴展,Redis Streams 受限於 single shard
規模 推薦
< 10K 線上使用者 Redis Streams(甚至 Redis Pub/Sub)
10K - 1M 線上 Redis Streams + Redis Cluster
1M - 10M 線上 Kafka + 多 IDC
> 10M 線上 Kafka + 自定義 partition routing

4. 單機 200 萬連線是特例不是常態

影片引 WhatsApp 2012 年的「單機 200 萬連線」當亮點,我必須提醒:這是 13 年前的數字,而且 WhatsApp 已經不這樣做了。

WhatsApp 現在的架構(從 2022 年公開分享推斷):

  • Erlang VM 跑 core routing(每台機器撐幾十萬 connection)
  • Go / Rust 跑 edge proxy(HTTP/3 + QUIC)
  • PostgreSQL 跑 user / message metadata
  • 自研儲存層跑 message body(基於 RocksDB)

現代要做到「單機高連線數」靠的是:

  • eBPF:繞過 kernel network stack,直接從 NIC 送到 user space
  • io_uring:Linux 5.x 後的非同步 IO,可以單執行緒處理幾十萬 socket
  • DPDK:Cloudflare / 抖音用的 user-space TCP

這些都跟影片時代的「Erlang magic」不一樣了。架構師的判斷:如果只是普通聊天產品,別想單機撐 200 萬 connection——用 100 台機器各撐 2 萬 connection 簡單太多。

5. 訊息順序保證:Snowflake ID 比 Lamport Clock 實用

影片點到「訊息順序」但沒展開。實務上有兩條路:

  • Lamport Clock / Vector Clock:每則訊息帶邏輯時戳,接收端按時戳排序。理論優雅,實作痛苦——client 端時鐘不可信、跨 server 時鐘漂移、衝突解決複雜
  • Snowflake ID + Server-side ordering:Twitter / Discord 的標準做法。每個 server 區段產生 monotonic ID(41 bits timestamp + 10 bits machine + 12 bits sequence),client 端收到後做 client-side sort + dedup

業界 100% 用後者。理由:

  • Snowflake ID 是 monotonic ascending,server-side sort 就是最終順序
  • 跨 server ordering:client 收到 N 個 server 的訊息後按 ID 排序,server 不用互相溝通
  • ID 自帶 timestamp,可以做 expire / TTL

我自己寫的 chat service 都用 Snowflake。Lamport Clock 只在分散式 transaction 場景才會用到。

6. Trade-off 反轉:Fan-out 卸到 CDN Edge

影片說「訊息量大就加機器」——現代架構師會說「先看能不能把 fan-out 卸到 CDN edge」。

Cloudflare Workers + Durable Objects 就是為這個場景設計的:

  • 每個 chat room 是一個 Durable Object
  • Durable Object 在 Cloudflare edge 自帶持久化
  • fan-out 從 edge 出去,到使用者 latency < 50ms(全球)
  • 不需要自己買機器、K8s cluster、Redis cluster

架構師的判斷順序應該是:

  1. 能不能用 SaaS(Sendbird、Stream Chat、PubNub)→ 用 SaaS
  2. 能不能用 Edge platform(Cloudflare Durable Objects、Vercel KV)→ 用 Edge
  3. 能不能用 managed open source(自架 Rocket.Chat + managed Kafka)→ 用 managed
  4. 最後才考慮自建(Go + Redis Streams + PostgreSQL)

影片畫的「25 台 Chat Server」設計在自建路徑才適用。如果今天重新做,我會先評估 SaaS 跟 Edge。

7. 一個面試官會問的反問

如果面試官聽完 Pub/Sub 設計反問:「如果某個使用者突然收到 100 萬個好友同時上線的通知,你怎麼辦?」

正確答案不是「rate limit」(太粗暴),也不是「分批送」(太慢),而是:

「Presence 通知不走主要訊息通道。Presence 是 best-effort、一致性要求低的,我會開一條獨立的 SSE channel(或者一個獨立 topic),batch 起來每 5 秒送一次合併後的狀態變更。訊息本體走另一條路徑,確保 100 萬個上線通知不會 backpressure 訊息本體的 latency。」

這個答案把「通道分離」講出來了——Presence、Typing、Message 是三種不同 SLA 的流量,混在一起會互相干擾。


架構師 takeaway:聊天系統在 2026 年已經高度商品化。SSE 取代 WebSocket、Redis Streams 取代 Kafka、Edge platform 取代自建——三大翻轉。如果不是要做 WhatsApp 等級的產品,架構師的判斷應該是「能不自己做的就不要自己做」,把精力花在產品差異化(emoji reaction、thread、AI 摘要)而不是底層 transport。

Other Related Posts:

POS 支付系統設計:三階段時序、Idempotency Key,以及和 Stripe 的差別

POS 支付系統設計:三階段時序、Idempotency Key,以及和 Stripe 的差別

這篇文章整理自 s09g 頻道的《[系統設計 Mock] POS payment service》系統設計 mock interview。整支影片只有十分鐘,但資訊密度很高——它講的不是「POS 是什麼」這種白話文,而是面試官會一直追問的設計細節:三階段時序、跨系統一致性、Idempotency Key 的物理意義。如果你是後端工程師要準備 system design interview,這幾個點一定要搞懂。


一、POS 不是「一個 API 收錢」——先選變體

影片一開頭就把題目切成三個變體:

  1. 全部在線變體(online-only):使...
17th Aug 2026 - Mark
建一個 Job Scheduler 之前要釐清的 11 個取捨點

建一個 Job Scheduler 之前要釐清的 11 個取捨點

任務調度系統(Task Scheduler / Job Scheduler)這種東西,看起來每家公司都有,用起來都差不多,但只要你認真去看一次原始碼,就會發現每個團隊的取捨都不一樣。

這篇文章不是要告訴你「任務調度是什麼」。網路上的科普文已經太多了。我要講的是:當我們真的坐下來要畫一張架構圖,每一個節點為什麼要這樣選。參考的影片是 s09g 的《system design — 任務調度系統》(約 29 分鐘),我把裡面 12 個章節的取捨整理成一條線,並加上我自己做後端這些年的看法。

我們的場景先講清楚:假設要替一個內部系統(10k 工程師規模)做一個任務調度平台。

...
17th Aug 2026 - Mark