當你在 Slack 頻道中輸入一段文字並按下 Enter,這條訊息通常會在不到 100 毫秒內出現在全球各地同頻道的數十、數百甚至數千名同事螢幕上。在極端情況下(例如全公司發布公告的公用頻道),單一工作區可能包含數萬名同時在線的成員。

如何保證在數千萬活躍用戶同時在線、數百萬個活躍頻道交錯通訊時,訊息能夠達到低延遲交付、嚴格有序性、訊息不丟失與離線增量收斂?

本文將從連線邊緣、業務驗證、多路廣播扇出(Fanout)到分片持久化,完整拆解 Slack 即時訊息系統的核心架構演進。


訊息投遞全鏈路全景圖

Slack 的核心訊息管線採用了「邊緣長連線閘道 + 集中業務 API + 記憶體頻道伺服器(Channel Server)+ 分片 MySQL」的分層解耦拓撲:

Slack 訊息投遞即時管線架構展示從客戶端 WebSocket 長連線、Edge Gateway 路由、Channel Server 廣播、Redis 緩存與 MySQL 分片持久化的全鏈路架構。EDGE & CONNECTION邊緣長連線與閘道Slack Client (App/Web)• 本地 SQLite 快取狀態• WebSocket 雙向長連線• 樂觀 UI 渲染 + SeqID 去重Edge Gateway (Envoy)• TLS 終結與連線保持• 負載均衡與地理路由• 驚群防禦與抖動重連Flannel Edge Cache• 使用者權限與 Workspace• 查詢快取降級主資料庫INGESTION & BUSINESS訊息寫入與業務驗證WebApp API Cluster• 頻道發言權限檢查• 富文本 / 檔案 Markdown 解析• 生成全域單調遞增 Msg IDMessage Server / Kafka• 寫入訊息持久化日誌• 非同步觸發 Bot 與 Webhook• 派發至搜尋檢索管線Presence & Typing• Ephemeral 短暫狀態• Redis In-Memory 廣播FANOUT & BROADCAST頻道多路廣播 (Fanout)Channel Server (Pub/Sub)• 維護 Channel 訂閱者清單• 記憶體扇出 (In-Memory Fanout)• 萬人大頻道分片廣播Push Notification (APNs/FCM)• 離線/未聚焦使用者過濾• 靜音與 DND 免打擾策略• 喚醒客戶端背景拉取WebSocket Gateway Egress• 下發至在線成員連線• 壓縮二進位 / JSON FrameSTORAGE & SYNC分片持久化與同步MySQL Shards (Vitess)• 依 Workspace/Channel 分片• 嚴格事務保證持久化• 歷史訊息長青封存Redis Message Cache• 最近 N 筆頻道訊息緩存• Unread Count 未讀計數器• 毫秒級快速頻道切換Search & Lucene Index• 頻道/私訊倒排索引• 實時增量建立全文檢索Slack 訊息投遞管線(手機檢視)1. 邊緣連線與閘道• Client WebSocket 保持雙向通道• Edge Gateway (Envoy) TLS 終結與路由• Flannel 邊緣快取 Workspace 權限• 樂觀 UI 渲染 + SeqID 去重防抖動2. 訊息寫入與業務驗證• WebApp 驗證頻道發言權限與 Markdown• 生成全域單調遞增 Message ID• Message Server 寫入 Kafka 事件日誌• 非同步觸發 Bot / Webhook / 搜尋索引3. 頻道多路廣播 (Fanout)• Channel Server 記憶體維護訂閱清單• 萬人大群分片並行廣播 (Fanout)• WebSocket 閘道下發在線連線• APNs / FCM 喚醒離線成員4. 分片持久化與狀態收斂• MySQL Shards (Vitess) 分片儲存• Redis 緩存近期訊息與未讀計數器• Lucene / ES 即時全文倒排索引• Delta Sync 離線斷線增量補償
圖 1:Slack 訊息投遞全鏈路架構 — 從 WebSocket 長連線、Channel Server 記憶體廣播到分片 MySQL 持久化

整個訊息投遞流程可精確劃分為四個核心階段:

  1. 邊緣連線與發送(Edge & Ingestion):客戶端透過 WebSocket 雙向通道傳輸,邊緣 Envoy 代理做 TLS 終結與長連線保持。
  2. 業務鑑權與訊息編序(WebApp & Ordering):API 服務驗證發言權限,透過分散式序列器產生單調遞增的 Message ID。
  3. 頻道記憶體扇出(Channel Server Fanout):依據頻道的訂閱清單,將訊息並行推送到目標成員所在的 WebSocket 連線節點。
  4. 多層快取與分片持久化(Storage & Flannel Sync):寫入分片 MySQL(Vitess)與 Redis 暫存,透過 Delta Sync 提供斷線重連補償。

1. 連線層架構:WebSocket 與邊緣 Gateway

在 Slack 早期架構中,客戶端依賴長時間輪詢(Long Polling)來拉取事件,這導致伺服器承載了海量的無效 HTTP 請求開銷。隨著用戶規模爆發,Slack 將通訊骨幹重構為基於 WebSocket 的全雙工長連線架構。

1.1 邊緣 Envoy 代理與長連線解耦

客戶端並非直接連線到底層業務伺服器,而是就近接入分佈在全球 PoP 節點的 Edge Gateway(基於 Envoy):

Slack 邊緣長連線 Gateway 拓撲圖展示 Client 透過 WebSocket over TLS 連接 Edge Gateway Envoy PoP,再經內部連線池轉發至 Ingress Tier 與 Channel Server。Slack Client (App/Web)WSSEdge Gateway (Envoy PoP)邊緣 TLS 卸載 + 連線池保持gRPCGateway Ingress Tier ➔ Channel Server內部多路復用長連線調度
  • TLS 終結(TLS Termination):在邊緣完成 SSL/TLS 握手,顯著減少客戶端到伺服器的 RTT 延遲。
  • 連線聚集(Connection Pooling):Edge Envoy 與內部核心叢集建立長期的 TCP/gRPC 連線池,避免內部網路因數百萬連線頻繁建立造成 Socket 耗盡。
  • 心跳保活與 Ping/Pong 探測:每 30 秒執行一次輕量 Application-level Ping/Pong 檢測 NAT 逾時與殭屍連線。

1.2 驚群防禦與抖動重連(Jitter Reconnect)

當某個資料中心或網路運營商發生抖動時,數百萬客戶端會同時發起斷線重連。如果沒有保護機制,瞬時連線風暴會直接打垮 Gateway 伺服器。

Slack 在客戶端實作了全抖動指數退避重連演算法(Full Jitter Exponential Backoff):

T_sleep = min(T_max, random(0, 2^attempt * T_base))
// Slack Client 抖動重連邏輯範例
function calculateReconnectDelay(
  attempt: number,
  baseMs = 500,
  maxMs = 30000,
): number {
  const exponential = Math.min(maxMs, baseMs * Math.pow(2, attempt));
  // 透過完全隨機化分散重連流量峰值
  return Math.floor(Math.random() * exponential);
}

2. 訊息寫入、鑑權與全域時序保證

當訊息封包透過 WebSocket 送達時,系統必須確認發送者的權限、格式化內容並賦予其嚴格的時序識別碼。

2.1 樂觀 UI 渲染與客戶端暫存 ID

為了提供極致的打字流暢度,Slack 客戶端採用 樂觀更新(Optimistic UI):

  1. 客戶端產生一個本地臨時 UUID client_msg_id。
  2. 立即將訊息渲染到本機聊天視窗,標記為 sending 狀態。
  3. 發送 Payload 包含 client_msg_id。
  4. 伺服器處理成功後回傳正式的伺服器訊息戳記,客戶端將狀態更新為 sent;若逾時未回執則顯示重發提示。

2.2 頻道時鐘與 Message Timestamp (ts)

Slack 使用精確到微秒的時間戳作為訊息的唯一識別碼與排序依據(例如 1725278400.000200):

-- 訊息資料表結構示意
CREATE TABLE channel_messages (
    workspace_id BIGINT UNSIGNED NOT NULL,
    channel_id BIGINT UNSIGNED NOT NULL,
    message_ts DECIMAL(16, 6) NOT NULL, -- 秒.微秒 (唯一主鍵之一)
    user_id BIGINT UNSIGNED NOT NULL,
    content TEXT NOT NULL,
    client_msg_id VARCHAR(64) NOT NULL,
    thread_ts DECIMAL(16, 6) DEFAULT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (workspace_id, channel_id, message_ts),
    UNIQUE KEY uk_client_msg (workspace_id, channel_id, client_msg_id)
) ENGINE=InnoDB;

3. 頻道記憶體扇出:Channel Server 與 Pub/Sub

訊息寫入驗證通過後,最關鍵的步驟是將訊息分發給頻道內的所有在線成員。

3.1 為什麼不用單純的 Redis Pub/Sub?

傳統的 Redis Pub/Sub 在面對數十萬個頻道的動態訂閱與巨型頻道時存在瓶頸:

  • 連線綁定負擔:如果每個使用者都向 Redis 訂閱其加入的數百個頻道,Redis 叢集的訂閱表將膨脹到數千萬級,記憶體開銷與廣播 CPU 暴增。
  • 大群廣播雪崩:當一個 5 萬人的大頻道發送訊息時,單一節點需要執行 5 萬次封包複製,造成單點 NIC 頻寬打滿。

3.2 Slack 的 Channel Server 拓撲

Slack 採用了自研的 Channel Server 叢集架構:

Slack Channel Server 批量扇出廣播架構圖展示 WebApp 發布訊息經 Channel Router 雜湊路由至 Channel Server,再批量合併發送給各 Gateway 與 Push 服務。WebApp API 發布訊息Channel Router (一致性雜湊)Channel Server (頻道記憶體狀態)持有成員在線清單與 Gateway 映射Gateway A (批量合併 3 人)一次 Socket 廣播 User 1, 2, 3Gateway B (批量合併 2 人)一次 Socket 廣播 User 4, 5Push Notification 服務非同步推送離線 User 6, 7 (APNs/FCM)💡 核心優化:避免逐人發送,將同一台 Gateway 的成員在記憶體中聚合,單台機器 1 次 RPC節省 90% 以上內部網路封包,徹底杜絕巨型頻道廣播風暴!
  1. 分片主管(Channel Sharding):每個 Channel 的在線成員狀態與訂閱清單由特定的 Channel Server 節點透過一致性雜湊負責。
  2. 網關批量聚合(Gateway Batching):Channel Server 不會為每個成員單獨建立 Socket 發送,而是將「同一台 Gateway 伺服器上的多個成員」合併為一筆發送指令,大幅節省內部 RPC 開銷。

4. 儲存架構:MySQL 分片與 Flannel 快取層

Slack 儲存著全球企業海量的歷史對話與搜尋索引,資料層必須具備水平擴展與極低讀取延遲。

4.1 Vitess 管理的 MySQL Sharding

Slack 將所有核心資料庫建構在 MySQL 之上,並透過 Vitess 進行分庫分表治理:

  • 分片鍵(Keyspace ID):以 workspace_id 作為一級分片鍵,保證同一個團隊的所有頻道與訊息落在同一個資料庫分片中,使得頻道內的查詢不需要進行跨分片分散-聚合(Scatter-Gather)。
  • 冷熱資料分離:近期 30 天的高頻存取訊息保留在 SSD 高效能節點;歷史封存訊息透過非同步批次搬移至壓縮歸檔庫存。

4.2 Flannel 邊緣快取引擎

為了避免每次使用者切換頻道或打開 App 都向 MySQL 發起繁重的查詢,Slack 開發了 Flannel 邊緣快取系統:

維度直查 MySQLFlannel 邊緣快取
讀取延遲15ms ~ 50ms< 2ms
資料組織關聯式資料表預先反正規化的 JSON/Protobuf 物件
快取失效快取穿透風險高訂閱 Channel Server 變更事件主動更新
冷啟動同步批次 SQL 查詢慢傳輸 Delta 增量快照

Flannel 節點常駐在 Edge Gateway 旁,快取了使用者所屬 Workspace 的成員清單、頻道列表、自訂 Emoji 與常用元資料,使 95% 以上的客戶端初始載入請求在邊緣即被命中。


5. 斷線修復與 Delta Sync 增量同步

在行動裝置網路切換(如 Wi-Fi 斷開轉為 4G/5G)或電腦休眠喚醒時,客戶端會經歷暫時的網路中斷。

5.1 增量同步協定 (Delta Sync Protocol)

客戶端重新建立 WebSocket 連線時,會帶上本機最後接收到的 last_read_ts:

Slack Delta Sync 增量重連同步協定時序圖展示 Client 帶 last_read_ts 重新連線,Edge Gateway 查詢 Flannel 計算 Gap 訊息並批次補齊回傳。Client (帶 last_read_ts)Re-connectEdge Gateway + Flannel 快取計算遺失 Gap Delta Messages回傳 Gap 批次訊息本地 SQLite 無縫合流!⚡ 弱網重連毫秒補齊,超過 500 筆門檻則優雅降級為全量快照背景同步
  1. Gap 檢測:若遺失的訊息數小於門檻值(例如 500 筆),伺服器直接回傳 JSON 格式的 Delta 增量列表,客戶端無縫插入本地 SQLite。
  2. 全量快照降級(Full Snapshot Fallback):若使用者離線數週或遺失訊息過多,系統觸發背景全量重新同步,先展示最近最新畫面,再非同步補齊歷史。

6. 架構總結與設計復盤

架構挑戰Slack 解決方案核心效益
百萬長連線開銷邊緣 Envoy TLS 終結 + WebSocket 連線池節省內部網路連線開銷,低於 100ms 全球延遲
大群廣播風暴Channel Server 分片 + Gateway 批量合併廣播避免單點網卡頻寬飽和與 Redis 訂閱膨脹
時序與去重微秒級單調遞增 message_ts + client_msg_id保證因果順序一致,杜絕網路抖動造成的重複訊息
儲存擴展性Vitess MySQL 分片(按 Workspace 隔離)支援數千億筆訊息儲存,本機事務高效執行
離線狀態收斂Flannel 快取 + Delta Sync 增量同步協定弱網與重連場景下瞬間恢復閱讀位置

Slack 的訊息管線證明了:即時通訊系統的架構核心在於邊緣長連線的隔離、廣播扇出的批量聚合,以及依租戶/工作區切分的確定性分片。


參考一手來源與延伸閱讀