Section 1:痛點 — 同步呼叫的三個問題
先從一個最常見的業務場景切入:用戶按下「下訂單」之後,系統需要完成哪些工作?
如果全部同步完成,會有三個致命問題:
問題一:延遲
全部做完才回應,用戶要等待 3–5 秒才能看到「訂單成功」。 現代用戶對超過 1 秒的等待就會感到不耐,轉換率直接下降。
問題二:耦合
Email 服務掛了,整個下訂單流程就失敗。推薦系統變慢, 用戶就要等更久。一個不相關的服務,影響了核心業務流程。
問題三:擴展難
推薦系統壓力大,你想水平擴展它,但它跟訂單服務是同一個呼叫鏈, 無法獨立部署和擴展。
類比:便利商店的訂單流程
同步流程像是你在便利商店結帳,店員要當著你的面聯絡倉庫確認庫存、 幫你寄 Email 收據、更新帳本、通知配送中心 — 全部做完你才能離開。
Message Queue 讓你:下完訂單立刻收到確認收據,後面的工作(Email、倉庫通知、 推薦系統更新)全部「非同步」在背景慢慢做,你不需要等。
Section 2:Message Queue 是什麼
Message Queue 是一個「中間人」服務,讓發訊息的一方(Producer)和接收訊息的一方(Consumer) 不需要互相知道對方的存在,也不需要同時在線。
Producer(生產者)
發送訊息到 Queue 的一方。在電商系統中是「訂單服務」。 發完就走,不等待任何 Consumer 的回應。
Broker / Queue
儲存和轉發訊息的中間服務。常見的有 Kafka、RabbitMQ、AWS SQS。 負責保證訊息的持久化和可靠傳遞。
Consumer(消費者)
從 Queue 取出訊息並處理的一方。可以有多個 Consumer 訂閱同一個 Topic, 各自獨立處理自己關心的部分。
引入 Message Queue 之後,下訂單流程變成這樣
用戶按下訂單 → 訂單服務扣庫存、建立記錄 → 發布order-created事件 → 立刻回傳成功。
Email 服務、倉庫服務、推薦系統、財務服務各自訂閱order-created事件,在背景依自己的速度處理。 任何一個服務掛了,都不影響下訂單本身;恢復後從 Queue 繼續消費即可。
Section 3:Kafka 核心概念
Kafka 是目前業界最主流的高吞吐量 Message Queue,由 LinkedIn 開源。 理解它的核心概念,等於理解了大多數 Message Queue 系統的共通設計邏輯。
Topic 和 Partition
Topic 是訊息的分類(例如 order-created), 每個 Topic 下面可以分成多個 Partition。Partition 是 Kafka 水平擴展的關鍵機制。
並行處理
多個 Consumer 同時處理不同 Partition,吞吐量隨 Partition 數量線性擴展。 Partition 越多,理論最大並行度越高。
分區內有序
同一個 Partition 內的訊息保持嚴格的順序(FIFO)。 跨 Partition 則不保證順序,這是設計上的取捨。
Key 決定分區
Producer 可以指定 message key,相同 key 的訊息會落在同一個 Partition, 確保同一訂單的所有事件按順序處理。
Offset 和 Consumer Group
Offset 是訊息在 Partition 內的位置編號(從 0 開始)。Consumer 透過 committed offset 記錄自己處理到哪裡, 重啟後從這個位置繼續,不會遺漏也不會重複(at-least-once 語意)。
Consumer Group 是 Kafka 最強大的概念之一。同一個 Group 內的 Consumer 共同「瓜分」Partition; 不同 Group 則各自獨立消費,互不影響。
Consumer Group 的實際運作
email-service(Group 1)
- • 訂閱
order-created - • Consumer A 處理 Partition 0、1
- • Consumer B 處理 Partition 2
- • 目的:發確認 Email
analytics-service(Group 2)
- • 訂閱相同的
order-created - • 有自己的 offset,獨立消費
- • 不影響 Group 1 的進度
- • 目的:更新銷售統計
同一份訊息,兩個 Group 各自收到一份,各自處理。這就是「發布 / 訂閱(Pub/Sub)」模型。
Section 4:Node.js 實作(kafkajs)
kafkajs 是 Node.js 生態系中最主流的 Kafka 客戶端, API 設計清晰,對 TypeScript 友好。以下是完整的 Producer 和 Consumer 實作範例。
錯誤處理:Dead Letter Queue(DLQ)
當訊息處理失敗時(例如 Email 服務 API 掛了),不應該無限重試阻塞正常訊息。 標準做法是設定重試次數上限,超過後送到 Dead Letter Queue,由人工或另一個服務處理。
Section 5:Kafka vs RabbitMQ — 選哪個?
Kafka 和 RabbitMQ 是最常被比較的兩個 Message Queue。它們的設計哲學根本不同, 適合的場景也截然不同。選錯工具比不用更麻煩。
| Kafka | RabbitMQ | |
|---|---|---|
| 設計目標 | 高吞吐量日誌流 | 任務佇列 |
| 訊息保留 | 可保留數天(可重播) | 消費後刪除 |
| 吞吐量 | 百萬 / 秒 | 萬 – 十萬 / 秒 |
| 學習曲線 | 陡峭 | 相對平緩 |
| 適合場景 | 事件溯源、日誌、大數據 | 任務分發、RPC |
| 雲端服務 | AWS MSK、Confluent Cloud | AWS SQS / SNS |
選 Kafka 的時機
- 每秒訊息量超過 10 萬
- 需要訊息重播(分析歷史事件)
- 事件溯源(Event Sourcing)架構
- 多個下游服務訂閱同一事件流
- 需要長期保留訊息供稽核
選 RabbitMQ(或 SQS)的時機
- 簡單的任務分發(影像壓縮、Email 批次)
- 需要複雜的路由規則(依內容分發)
- 訊息消費後不需要保留
- 團隊對 Kafka 不熟悉
- 想快速在 AWS 上啟動用 SQS
Section 6:常見的事件驅動模式
引入 Message Queue 之後,系統架構通常會往事件驅動的方向演進。以下三個模式是業界最常見的應用方式。
1事件溯源(Event Sourcing)
傳統做法是儲存「當前狀態」(例如:訂單狀態 = 已出貨)。 Event Sourcing 改成儲存所有「事件」,從事件序列重建當前狀態。 這讓你可以回放任何時間點的狀態,對稽核和 Debug 非常有價值。
2CQRS(Command Query Responsibility Segregation)
讀寫分離的架構模式。「寫入操作(Command)」和「讀取操作(Query)」使用不同的資料模型和儲存, 透過 Message Queue 同步。讀取端可以針對查詢場景做專門優化。
3Saga Pattern(分散式交易)
微服務架構下,一個業務操作跨越多個服務時,無法用傳統的資料庫交易(ACID)保證一致性。 Saga 透過一系列本地交易 + 補償操作(Compensating Transaction)來實現最終一致性。
什麼時候不需要 Message Queue?
Message Queue 解決了很多問題,但也引入了額外的複雜度(Broker 的維運成本、最終一致性的複雜邏輯、 Debug 難度提高)。以下場景用同步呼叫就好,過早引入 Kafka 是反模式:
- •單體應用、小流量:用 Queue 增加了運維複雜度,卻沒帶來對應的好處。
- •強一致性需求:銀行轉帳必須「扣款成功才算轉帳成功」,不能用最終一致性。
- •需要立即回傳結果:用戶問「我的庫存還剩幾個」,這種同步查詢不適合 Queue。