🎯 什麼情境該想到我
當你「只想接收通道中符合特定條件的訊息,其餘留在通道給別人處理」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:讓消費者指定一組選擇條件(selector),只接收符合條件的訊息,不符合的留在通道。
做法 / 結構要點:
- 消費者宣告選擇條件(通常對訊息標頭 / 屬性做判斷)。
- 訊息系統只把符合條件的訊息投遞給它。
- 不符合的訊息留給其他消費者或稍後處理。
★★ 先問:這件事該由 broker 做還是消費者做
① ★★ broker 端過濾(最好)
RabbitMQ topic/headers exchange、SNS filter policy、NATS 通配
→ ★ 不符條件的訊息根本不會送給你:零頻寬、零 CPU
② ★ 分流成不同通道(Kafka 上唯一的辦法)
→ ★ 這就是 [[資料型別通道]]
③ 消費端過濾(本模式)
→ ★★ 白收所有訊息:頻寬、反序列化 CPU、以及「ack 掉不要的訊息」的成本
★★ Kafka 幾乎沒有 broker 端過濾(只有分區),所以在 Kafka 上
「選擇性消費」實際上只有 ② 和 ③。
③ 的成本可以估算:
2 KB × 10,000 則/秒 × 只有 5% 有用
→ ★ 白收 19 MB/s 網路 + 9,500 則/秒的反序列化
→ ★ 值不值得分流成新的 topic?用這個數字決定,不要憑感覺
★★ 這個模式在點對點通道上的致命問題
★ 一個佇列,兩個選擇性消費者:
A 只收 region=TW,B 只收 region=JP
→ ★★ 來了一則 region=US 的訊息
→ A 拒絕(nack requeue)→ B 拒絕 → A 又拿到 → ★ 無限迴圈
→ ★ 而且它會一直卡在隊首,把後面全部堵住(poison message)
★★ 這是選擇性消費者最容易踩的坑:
在競爭型(點對點)通道上做選擇性消費,沒被任何人接受的訊息會無限循環。
★ 對策只有兩個:
① 每個消費者的條件必須完全覆蓋所有可能的訊息(★ 而這很難保證);
② ★ 不要在同一個佇列上放多個選擇性消費者——改用 ① 或 ②(broker 過濾/分流)。
★ Go 的實作:不要的訊息要 ack,不要 nack
func handle(ctx context.Context, msg Message) error {
var e Envelope
if err := json.Unmarshal(msg.Body, &e); err != nil { … }
if !interested(e) {
metrics.Skipped.WithLabelValues(e.Type).Inc() // ★ 一定要計數
return msg.Ack() // ★★ ack 掉,不要 nack —— nack 會造成無限迴圈
}
…
}★★ 「不感興趣就 ack」在發布訂閱通道上是正確的(你有自己的 queue/offset,
ack 只影響你)。
⚠️ 但在共用佇列上,ack 表示「我處理掉了」——別人就看不到這則訊息了。
→ ★ 所以選擇性消費者幾乎只適用於「每個消費者有自己的通道」的情境。
★★ 過濾條件在消費端 vs 在 broker:一個常被忽略的差異
★ 條件在消費端 → 條件改變只需要部署消費者(★ 自己可控)
★ 條件在 broker → 條件改變要改基礎設施(★ 可能要跨團隊、可能要 IaC PR)
⚠️ 但也意味著「這個消費者收什麼」是可觀測的(在 broker 的 binding 上看得到)
★ 判準:條件多久改一次、誰擁有它?
自己擁有且常改 → 消費端。
穩定且需要被別人看到(治理、合規)→ broker binding。
★ 兄弟辨析
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
選擇通常只能對標頭 / 屬性做,不宜對大 body 內容做(效能)。
-
條件互斥要設計好,否則某些訊息可能沒人收(卡在通道)。
-
是在消費端過濾;也可改在來源端用不同通道分流。
-
★★ 不要在同一個競爭型佇列上放多個選擇性消費者。沒被任何人接受的訊息會無限循環並堵住隊首。
-
★★ 不感興趣的訊息要 ack,不要 nack。 nack 會造成無限迴圈。
-
★ 但在共用佇列上 ack 表示「我處理掉了」,別人就看不到了——所以這個模式幾乎只適用於「每個消費者有自己的通道」。
-
★★ Kafka 幾乎沒有 broker 端過濾。在 Kafka 上只能分流成不同 topic,或消費端全收再丟。
-
★ 消費端過濾的成本可以估算(訊息大小 × 速率 × 丟棄比例)。用數字決定要不要分流。
-
★ 跳過的訊息一定要計數,否則「這筆為什麼沒處理」查不出來。
-
★ 條件放消費端可自己控制、放 broker 則可被觀測。依「誰擁有它、多久改一次」決定。
-
優先考慮 資料型別通道:用通道分開,根本不需要選擇。
🔗 相關工具
- 訊息分派器 Message Dispatcher(同家族,改由單一消費者集中分派)
- 競爭消費者 Competing Consumers(同家族,另一種多消費者分工方式)
- 企業整合模式
- 訊息過濾器 —— 獨立元件版
- 資料型別通道 —— ★ 用通道分開,不需要選擇
- 競爭消費者 —— ★ 混用會造成無限迴圈
- 內容路由器 —— 在中間分流
- 死信通道 —— 無人接受的訊息該去哪