Chapter 12: Design a Chat System
1. 問題釐清與需求確認
在設計聊天系統之前,需要釐清以下需求:
- 支援一對一聊天與小型群組聊天(上限 100 人)。
- 訊息型態:文字(單則 ≤ 10萬 字元)。
- 系統規模:5千萬日活躍用戶。
- 訊息需永久保存。
- 初期不要求端到端加密。
- 功能:多裝置登入、在線狀態、推播通知。
1. Requirements Clarification
Before designing the chat system, confirm the requirements:
- Support both one-to-one chat and small group chat (up to 100 users).
- Message type: text only (≤100,000 characters per message).
- Scale: 50 million daily active users (DAU).
- Message history must be stored permanently.
- End-to-end encryption is not required initially.
- Features: multi-device login, presence management, push notifications.
2. 高階架構設計
系統核心組件:
- Chat servers:維持 WebSocket 連線,處理即時訊息傳遞。
- Presence servers:管理在線/離線狀態。
- Push notification servers:發送離線推播。
- Key-value stores:儲存聊天訊息。
- API servers:處理登入、好友清單等非即時功能。
傳輸模式:
- 發送端使用 HTTP (keep-alive)。
- 接收端依靠 WebSocket 由伺服器主動推送訊息。
2. High-Level Architecture
Core system components:
- Chat servers: Maintain WebSocket connections for real-time messaging.
- Presence servers: Manage online/offline status.
- Push notification servers: Send push notifications to offline users.
- Key-value stores: Store chat history.
- API servers: Handle login, profiles, and friend lists.
Communication model:
- Sender → server via HTTP (keep-alive).
- Server → receiver via WebSocket for real-time delivery.
3. 協定比較
- Polling:定時詢問,效率低。
- Long Polling:伺服器保持連線直到有訊息,再回應;回應後連線必須關閉(HTTP 限制)。
- WebSocket:一次握手後可建立持久雙向連線,延遲最低。
補充:Long Polling 必須關閉連線,因為 HTTP 協定是一次請求/回應。
3. Protocol Comparison
- Polling: Periodic requests; inefficient.
- Long Polling: Server holds the request until a message is available, then responds. The connection must close afterward due to HTTP’s request/response model.
- WebSocket: Establishes a persistent, full-duplex connection after a single handshake, providing minimal latency.
Note: Long Polling must close the connection because HTTP inherently allows only one request per response.
4. 資料儲存設計
- 用戶資料:關聯式資料庫(分片 + 複製)。
- 訊息資料:Key-Value Store(如 HBase、Cassandra)。
分片 (Sharding)
- 水平切分資料,分散到多個資料庫節點。
- 常見方式:範圍分片、雜湊分片、目錄分片。
- 群組聊天常用 channel_id 作為分片鍵,以確保同群組訊息在同一 shard。
- 每個 shard 通常有多副本以提升可靠性。
4. Data Storage Design
- User data: Relational databases with sharding and replication.
- Messages: Key-value stores (e.g., HBase, Cassandra).
Sharding
- Horizontally partition data across multiple database nodes.
- Common methods: range-based, hash-based, directory-based.
- Group chats typically shard by
channel_id
to keep all group messages in the same shard. - Each shard usually has replicas for high availability.
5. 訊息 ID 生成策略
- Global Sequence:如 Snowflake,系統全域唯一,但需要協調。
- Local Sequence:在單一對話/群組內遞增即可。
- 優點:效能好、簡單、可擴展。
- 缺點:無法跨群組排序。
- 常見 schema:
(channel_id, message_id)
。
5. Message ID Generation
- Global Sequence: e.g., Snowflake; globally unique but requires coordination.
- Local Sequence: Incremental within a single conversation or group.
- Pros: efficient, simple, scalable.
- Cons: cannot provide global ordering across groups.
- Common schema:
(channel_id, message_id)
.
6. 訊息傳遞流程
一對一聊天:
- 發送者 → Chat Server。
- 生成 message_id。
- 存入 KV store 與同步佇列。
- 接收者在線 → WebSocket 傳送。
- 接收者離線 → 推播通知。
群組聊天:
- 訊息存為
(channel_id, message_id)
。 - 透過 Chat Server 或 Pub/Sub 系統 fanout 給成員。
- 在線成員透過 WebSocket 即時接收。
- 離線成員上線時同步歷史訊息。
6. Message Delivery Flow
One-to-one chat:
- Sender → Chat Server.
- Message ID generated.
- Stored in KV store and sync queue.
- Online receiver → delivered via WebSocket.
- Offline receiver → push notification sent.
Group chat:
- Stored as
(channel_id, message_id)
. - Distributed via Chat Server or Pub/Sub.
- Online members get real-time WebSocket delivery.
- Offline members sync messages on reconnection.
7. Fanout 技術
- 伺服器直接推送:小群組。
- Pub/Sub 模型:中型群組。
- Lazy Fanout:大型群組。
實務系統多採混合策略,例如 Slack 用 Pub/Sub,Discord 小群組即時推送,大群組 Lazy Fanout。
7. Fanout Techniques
- Server direct push: For small groups.
- Pub/Sub model: For medium-sized groups.
- Lazy fanout: For large groups.
In practice, hybrid approaches are common. For example, Slack uses Pub/Sub, while Discord pushes small groups in real time but applies lazy fanout to large communities.
8. Presence 管理
- 客戶端定期傳送 heartbeat。
- 若超時未收到 → 標記為離線。
- 狀態變化透過 Pub/Sub 廣播。
- 大型群組避免全量廣播,僅進群或刷新時查詢。
8. Presence Management
- Clients periodically send heartbeat signals.
- If no heartbeat within the threshold → mark offline.
- Status updates broadcast via Pub/Sub.
- Large groups avoid full broadcasts, checking presence only on entry or refresh.
9. 系統可靠性與延伸設計
- Service Discovery:ZooKeeper 幫助選擇最佳伺服器。
- 容錯:伺服器宕機時,自動重新連線。
- 訊息重送:透過 retry 與 queue 確保可靠性。
延伸功能:
- 支援媒體訊息(壓縮、雲端存儲、縮圖)。
- 端到端加密(WhatsApp 模型)。
- 用戶端快取。
- 全球分散式快取(如 Slack)。
9. Reliability and Extensions
- Service Discovery: ZooKeeper helps route clients to optimal servers.
- Fault Tolerance: Clients reconnect automatically if servers fail.
- Message Retries: Retry and queues ensure reliability.
Extensions:
- Media support (compression, cloud storage, thumbnails).
- End-to-end encryption (WhatsApp model).
- Client-side caching.
- Geo-distributed caching (e.g., Slack).
10. 總結
- WebSocket 是核心技術,Long Polling 僅為過渡。
- Long Polling 必須關閉連線,因為 HTTP 限制。
- Fanout 是群組訊息的核心,根據群組大小選擇策略。
- Local Sequence 符合聊天應用需求,避免全域協調。
- Sharding 是大規模訊息系統的必備設計。
- 系統需兼顧低延遲、可擴展性與可靠性。
10. Summary
- WebSocket is the backbone of real-time chat; Long Polling is only a workaround.
- Long Polling connections must close due to HTTP limitations.
- Fanout is essential for group messaging, with strategies chosen by group size.
- Local Sequence fits chat use cases, avoiding global coordination.
- Sharding is critical for scaling large-scale messaging systems.
- The design must balance low latency, scalability, and reliability.