前言

在微服務架構中,當系統從單體(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 CacheZero-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 Producerenable.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.msmax.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-onceExactly-once 交付語意、強大的 Key-Value Store 機制,以及基於 Raft 的高可用性。
  • 不同於 Kafka 的笨重,NATS JetStream 只包含單一 binary 執行檔(小於 20MB),不需依賴外部 Zookeeper 或任何東西,設定極度簡單。

3. 致命弱點:生態系與市佔率

NATS 技術設計雖美,但與 Kafka Connect/Streams 龐大的資料整合生態(Debezium、Spark、Flink)相比,社群豐富度仍有不小的差距,引入前需評估整合成本。


四、實務情境選型指南

👉 選擇 RabbitMQ 的情境

  1. 複雜非同步任務佇列:影像轉檔、寄送 Email/SMS、報表產生等需要追蹤狀態的工作。
  2. 需要精密路由:同一筆訂單狀態更新,依類別分送不同處理佇列;需要 Delayed Queue 與 Dead-Letter Queue(DLX)的業務重試機制。
  3. 絕對的任務交付保障:必須確認任務 100% 執行完畢(Manual ACK + NACK + DLX),且流量規模在合理範圍內(每秒數萬 QPS 以內)。

👉 選擇 Kafka 的情境

  1. 微服務 Event Sourcing(事件溯源):系統唯一真理來源是事件流,任何微服務重啟後都能重播(Replay)歷史 Offset 還原狀態。
  2. 海量日誌與大數據吞吐(10 萬+ QPS):Clickstream 追蹤、Log 收集入 ELK 等需要 Write-heavy 的場景。
  3. 需要串流處理管道:利用 Kafka Connect 建立 CDC 資料管道,或以 Kafka Streams / ksqlDB 進行即時串流聚合。

👉 選擇 NATS 的情境

  1. Edge Computing / IoT 邊緣計算:資源受限節點(如 Raspberry Pi),Kafka 的 JVM 記憶體開銷是直接 KO 等級,而 NATS 的單一 binary 是唯一救星。
  2. K8s 內部的極速微服務通訊:追求微秒等級的 Service-to-Service 通訊,取代部分 gRPC 或 HTTP 的同步呼叫。
  3. 小團隊,需要簡單的持久化串流:想要 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 圖表時,最堅定落筆的依據。