訊息佇列架構大對決:Kafka vs RabbitMQ vs NATS 的底層哲學與選型策略
前言
在微服務架構中,當系統從單體(Monolithic)走向分散式,服務之間的通訊模式從同步的 HTTP/gRPC API 呼叫,逐漸演變為非同步的事件驅動(Event-Driven)架構。此時,Message Queue(訊息佇列,簡稱 MQ) 就成了系統的中央神經索,負責解耦(Decoupling)、削峰(Load Leveling)與非同步處理。
然而,市面上的 MQ 五花八門。若從資深架構師的底層視角來看,RabbitMQ、Kafka 與 NATS 這三者根本不能互換,因為它們代表了三種截然不同的分散式設計哲學。選錯 MQ,不只是效能低下的問題,更可能面臨資料遺失與無解的維運死胡同。本文將帶你剖析它們的底層機制,找出正確的選型策略。
零、訊息交付語意(Delivery Semantics)——選型前的認知基礎
在深入比較各 MQ 之前,必須先理解貫穿全文的核心概念:訊息交付語意。三大語意的選擇,直接決定了你的系統在崩潰或重傳時的資料正確性保障:
| 語意 | 說明 | 風險 |
|---|---|---|
| At-most-once | 訊息最多交付一次,不重傳 | 訊息可能永久遺失 |
| At-least-once | 訊息至少交付一次,失敗會重傳 | 訊息可能重複,Consumer 必須實作冪等性(Idempotency) |
| Exactly-once | 訊息精確交付一次,不多不少 | 實現最複雜,通常有效能代價 |
At-least-once 是業界最廣泛採用的策略,但工程師必須對每一個 Consumer 的業務邏輯做冪等設計(例如:確保同一筆 Payment ID 不會被扣款兩次)。
一、RabbitMQ:傳統企業級的「智慧郵局」
RabbitMQ 採用 Erlang 語言開發,是最老牌、功能最完整的企業級訊息中介軟體。設計哲學是 「Smart Broker / Dumb Consumer(聰明的伺服端 / 愚笨的消費端)」。
1. 複雜且強大的路由機制(Routing)
RabbitMQ 的核心實作了 AMQP 協議。Producer 寫入訊息時並非直接丟進 Queue,而是交給 Exchange(交換機)。透過定義 Direct、Fanout、Topic 或是 Headers 規則,Exchange 能精準決定一筆訊息該複製送到哪些 Queue,極度適合處理複雜的商業邏輯分發。
2. ACK 機制與正確的重試設計
RabbitMQ 是一座真正的郵局,每一封信派出去後,它會等待 Consumer 回傳 Acknowledgement。實際上,ACK 策略有三種層次,選錯是生產事故的常見根源:
- 自動 ACK(Auto-ack):Consumer 收到訊息即算完成,若此刻 Consumer 崩潰,該筆資料永久消失。
- 手動 ACK(Manual ACK):處理完成後才 ACK,若 Consumer 死迴圈不回應,訊息會永久停在 Unacked 狀態佔記憶體。
- ✅ NACK + Dead-Letter Exchange(DLX):這才是最健壯的業界最佳實踐。業務邏輯失敗時,呼叫 NACK 將訊息路由至一個「死信佇列(Dead-Letter Queue)」,供後續人工介入或重試分析,絕不讓失敗訊息憑空消失。
⚠️ 儲存特性:訊息被 Consumer 成功消耗(ACK)後,就會立刻從記憶體與磁碟中刪除。若 Queue 堆積了千萬筆訊息(Backlog)未處理,RabbitMQ 記憶體會撐爆,進而觸發 Page Out 機制,導致叢集效能直線墜崖。RabbitMQ 是一個「用過即丟」的工具,而非資料庫。
3. 高可用架構的演進:Classic Mirror vs Quorum Queue
RabbitMQ 的高可用(HA)架構分兩個世代,不可混淆:
- Classic Mirror Queue(舊):每個 Queue 的資料會被鏡像(Mirror)到所有叢集節點。然而,所有寫入仍需經過 Master 節點轉發,單機寫入仍是瓶頸,且 Mirror 有腦裂風險。
- Quorum Queue(新,3.8+):底層基於 Raft 共識演算法,採用 Leader + Follower 的分散式日誌複製,寫入需多數節點確認,正式解決了腦裂問題與資料遺失的風險。新系統應一律使用 Quorum Queue 取代舊有的 Classic Mirror 架構。
二、Kafka:為巨量資料流而生的「分散式提交日誌」
LinkedIn 當年因為傳統 MQ 無法負荷海量的使用者行為 Log,所以採用 Scala/Java 從零打造了 Kafka。設計哲學與 RabbitMQ 完全顛倒:「Dumb Broker / Smart Consumer(愚笨的伺服端 / 聰明的消費端)」。
1. 追加寫入的分散式日誌(Append-only Commit Log)
Kafka 的底層只是一個不斷往尾端追加寫入的磁碟日誌檔案(Commit Log)。
- 所有訊息存放於磁碟,依賴作業系統的 Page Cache 與 Zero-Copy(零拷貝技術) 達成每秒百萬 QPS 的恐怖讀寫效能。
- 資料不因讀取而刪除:即使 Consumer 讀過了,訊息依然保留在磁碟上,直到觸發 Retention Policy(如保留 7 天)才清除。這賦予了它「事件回溯(Time Travel / Event Sourcing)」的超能力。
2. Topic 分區(Partition)帶來的無限擴展
Kafka 將一個 Topic 切分成多個 Partition,散佈在數十台 Broker 上,使得寫入吞吐量可以近乎無限地水平擴展(Scale-out)。
3. 訊息交付語意的進化:Exactly-once Semantics (EOS)
Kafka 的交付語意是業界演進最快的:
- Kafka 0.11 以前:At-least-once,需開發者自行做冪等設計。
- Kafka 0.11+:透過同時啟用 Idempotent Producer(
enable.idempotence=true)與 Transactional API,正式達成 Exactly-once Semantics(EOS),可保障「Producer 到 Broker 到 Consumer 整條路徑上的精確一次投遞」。代價是有額外的效能與複雜度開銷,需評估是否符合業務需求。
4. Kafka 生態系的真正護城河:Connect & Streams
Kafka 的護城河不只是吞吐量,而在於其完整的串流資料生態系:
- Kafka Connect:內建的 Source/Sink Connector 框架。透過 Debezium Connector 可直接捕獲 PostgreSQL/MySQL 的 Binlog 變更,完全零程式碼地建立 CDC 資料管道;透過 S3/Elasticsearch Sink Connector 可直接把資料流匯出至雲端儲存或搜尋引擎。
- Kafka Streams / ksqlDB:提供在 Kafka Topic 上直接進行有狀態串流運算(Stateful Stream Processing)的能力,可以在 Kafka 內部做 Windowing、Join、Aggregation,甚至取代部分 Apache Spark Streaming 的業務場景。
5. 實務維運的深坑:Rebalance 風暴
⚠️ 架構師警告:當 Consumer Group 的節點數量改變時,Kafka 必須觸發 Rebalance(重新平衡分配 Partition),在此期間所有的消費都會停滯(Stop the world)。若設定不當(特別是
session.timeout.ms與max.poll.interval.ms設定錯誤),頻繁的 Rebalance 風暴會導致系統嚴重的延遲抖動。Kafka 需要 Zookeeper(或新版 KRaft 模式)來維護叢集 metadata,整體維運與建置成本是三者中最高的。
三、NATS(含 JetStream):雲原生的輕量級黑馬
NATS 最初是用 Go 語言為了 Cloud Foundry 開發的,代表了雲原生時代「極致輕量、極致降噪」的哲學理念。
1. Core NATS:純粹的神經系統(At-most-once)
最原始的 Core NATS 強調 At-most-once。它完全在記憶體運作,沒有持久化,遇到沒有人訂閱(No Subscribers)的訊息便瞬間丟棄。這賦予了它怪物般的超低延遲(微秒等級)與單機千萬級的 QPS,在即時 IoT 遙測(Telemetry)或微服務 RPC 通訊中是完美的短跑選手。
2. NATS JetStream:補足持久化與更高語意的拼圖
為了彌補無法可靠留存訊息的缺陷,NATS 團隊推出了內建的 JetStream 引擎。
- 引入訊息持久化、At-least-once 與 Exactly-once 交付語意、強大的 Key-Value Store 機制,以及基於 Raft 的高可用性。
- 不同於 Kafka 的笨重,NATS JetStream 只包含單一 binary 執行檔(小於 20MB),不需依賴外部 Zookeeper 或任何東西,設定極度簡單。
3. 致命弱點:生態系與市佔率
NATS 技術設計雖美,但與 Kafka Connect/Streams 龐大的資料整合生態(Debezium、Spark、Flink)相比,社群豐富度仍有不小的差距,引入前需評估整合成本。
四、實務情境選型指南
👉 選擇 RabbitMQ 的情境
- 複雜非同步任務佇列:影像轉檔、寄送 Email/SMS、報表產生等需要追蹤狀態的工作。
- 需要精密路由:同一筆訂單狀態更新,依類別分送不同處理佇列;需要 Delayed Queue 與 Dead-Letter Queue(DLX)的業務重試機制。
- 絕對的任務交付保障:必須確認任務 100% 執行完畢(Manual ACK + NACK + DLX),且流量規模在合理範圍內(每秒數萬 QPS 以內)。
👉 選擇 Kafka 的情境
- 微服務 Event Sourcing(事件溯源):系統唯一真理來源是事件流,任何微服務重啟後都能重播(Replay)歷史 Offset 還原狀態。
- 海量日誌與大數據吞吐(10 萬+ QPS):Clickstream 追蹤、Log 收集入 ELK 等需要 Write-heavy 的場景。
- 需要串流處理管道:利用 Kafka Connect 建立 CDC 資料管道,或以 Kafka Streams / ksqlDB 進行即時串流聚合。
👉 選擇 NATS 的情境
- Edge Computing / IoT 邊緣計算:資源受限節點(如 Raspberry Pi),Kafka 的 JVM 記憶體開銷是直接 KO 等級,而 NATS 的單一 binary 是唯一救星。
- K8s 內部的極速微服務通訊:追求微秒等級的 Service-to-Service 通訊,取代部分 gRPC 或 HTTP 的同步呼叫。
- 小團隊,需要簡單的持久化串流:想要 Kafka 的 JetStream 功能,但沒有人力維護 ZooKeeper/KRaft,徹底迴避 Rebalance 風暴的複雜度。
結論
沒有萬能的 MQ,只有最適合業務形態的管道。
- RabbitMQ:精密的 Exchange 路由 + NACK/DLX 死信機制,是可靠任務派發的最佳郵局。
- Kafka:Append-only Commit Log + Page Cache + Connect/Streams 生態,是事件驅動架構的不朽脊椎。
- NATS (JetStream):一顆 <20MB 的 binary,在 IoT 邊緣節點與 K8s 內部通訊的戰場上幾乎無與倫比。
釐清交付語意(At-most-once / At-least-once / Exactly-once)的業務需求是選型的起點,再搭配吞吐量、維運複雜度與生態整合需求,才是資深架構師在繪製 Topology 圖表時,最堅定落筆的依據。











