🎯 什麼情境該想到我
當你「要確保一則訊息恰好被一個接收者消費一次」的時候(例如下一筆命令、派一件工作,不能被重複執行)。
⚙️ 怎麼用(步驟 / 公式)
意圖:用一個保證「每則訊息只被一個接收者取用」的通道來傳送訊息。
做法 / 結構要點:
- 通道上可以有多個接收者,但每則訊息只會交給其中一個。
- 多個接收者可用來分攤負載(競爭消費者),彼此瓜分訊息、各做各的。
- 適合命令式或工作分派的訊息,強調「只處理一次」。
★ 各家 broker 怎麼做到「一則訊息只給一個接收者」
| broker | 機制 | ★ 關鍵限制 |
|---|---|---|
| RabbitMQ | queue + 多個 consumer(round-robin/prefetch) | ★ 順序不保證(多個 consumer 並行處理) |
| SQS | queue + visibility timeout | ★ 標準佇列不保證順序也可能重複;FIFO 佇列有吞吐上限 |
| Kafka | topic + 同一個 consumer group(每個分區只指派給組內一個成員) | ★★ 並行度上限 = 分區數 |
| NATS JetStream | queue group/durable consumer | — |
★★ Kafka 的關鍵差異:並行度被分區數鎖死
topic 有 3 個分區,consumer group 有 5 個成員
→ ★ 3 個成員各拿一個分區,另外 2 個「完全閒置」
想提高並行度 → 必須增加分區數
⚠️ 但分區數「只能加不能減」,而且加分區會改變 key → partition 的對應
→ ★ 同一個 key 的新訊息可能落到不同分區,破壞既有的順序保證
★ 所以 Kafka 的分區數是一個要提前想清楚的容量決定,不像 RabbitMQ 可以隨時加 consumer。
實務做法:一開始就開比預期多的分區(例如預估並行度的 2–3 倍),空著不用的成本很低。
★★ 順序 vs 並行:這是點對點通道無法迴避的取捨
一個消費者 → ★ 嚴格順序,但吞吐 = 單一消費者的處理速度
多個消費者 → ★ 高吞吐,但同一通道的訊息會被並行處理 ⇒ 順序消失
// ★ 務實的折衷:只保證「同一個實體」的順序
// 用實體 ID 當分區鍵 → 同一張訂單的事件必然進同一個分區 → 必然被同一個消費者依序處理
// 不同訂單之間可以完全並行
producer.Send(ctx, Message{Key: []byte(order.ID), Value: body})★ 更穩健的做法:讓處理與順序無關。
訊息帶版本號/OccurredAt,消費端丟棄比已處理狀態更舊的訊息
——這樣即使亂序也不會出錯,而且可以完全並行(見 訊息序列、工具-資料分區)。
★ 兩個容易被忽略的實務細節
① ★ RabbitMQ 的 prefetch(QoS)預設是「不限制」
→ 一個消費者可能把整個佇列抓進自己的記憶體,其他消費者拿不到訊息
→ ★ 一定要設 basic.qos(prefetch_count)。實務起點:等於併發 worker 數
② ★ SQS 的 visibility timeout 決定「重送」的時機
預設 30 秒(查證 2026-09-25,最大 12 小時)
→ 處理超過 30 秒 → 訊息對別人再次可見 → ★ 同一則被兩個消費者處理
★ 兄弟辨析
| 差別 | |
|---|---|
| 發布訂閱通道 | ★ 每個訂閱者都拿到一份;點對點是「只有一個人拿到」 |
| 競爭消費者 | ★ 這是點對點通道的消費端模式(多個消費者搶同一個通道) |
| 訊息分派器 | 一個消費者收下、在行程內分給多個 worker(★ 不是多個消費者) |
| 選擇性消費者 | 消費者只挑符合條件的(★ 會讓「一則只給一人」的語義變複雜) |
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
若你要「同一則訊息讓多方都收到」(事件廣播),這模式不適用,該用發布訂閱通道。
-
多個競爭消費者時,訊息的處理順序通常無法保證。
-
★★ Kafka 的並行度上限是分區數。多出來的消費者完全閒置,而分區數只能加不能減。
-
★ 加 Kafka 分區會改變 key → partition 的對應,破壞既有的順序保證。分區數要提前想清楚。
-
★★ 多個消費者 ⇒ 順序消失。要順序就只能一個消費者,或用實體 ID 當分區鍵限縮順序範圍。
-
★ RabbitMQ 的 prefetch 預設不限制,一個消費者會把佇列抓光。一定要設
basic.qos。 -
★ SQS 標準佇列不保證順序也可能重複。需要順序要用 FIFO 佇列(有吞吐上限)。
-
★ 處理時間超過 visibility timeout(SQS 預設 30 秒)會造成重複處理。
-
更穩健的做法是「讓處理與順序無關」:帶版本號,丟棄過舊的訊息。
🔗 相關工具
- 發布訂閱通道 Publish-Subscribe Channel(對照:一則訊息廣播給所有訂閱者)
- 訊息通道 Message Channel(點對點通道是通道的一種)
- 死信通道 Dead Letter Channel(處理失敗時把訊息移走,避免卡住消費者)
- 企業整合模式
- 發布訂閱通道 —— 對照:每個訂閱者一份
- 競爭消費者 —— 點對點通道的消費端擴展
- 訊息分派器 —— 行程內分派,不是多消費者
- 訊息序列 —— 讓處理與順序無關
- 工具-資料分區 —— 分區鍵決定順序粒度與並行度