🎯 什麼情境該想到我
當你「要根據訊息的內容,把它送到正確的目的通道」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:以訊息的內容為依據,將它路由到不同的通道。
做法要點:
- 路由器檢查訊息內容(某些欄位、型別、值等)。
- 依照預定規則決定唯一的輸出通道,並把訊息原樣轉送。
- 不改動訊息內容,只決定去向。
- 適用於處理步驟由不同系統負責、或資料需依類別分流的情境。
★ Go 的實作:三十行就是一個內容路由器
type Route struct {
Match func(Envelope) bool
Topic string
}
var routes = []Route{
{func(e Envelope) bool { return e.Type == "orders.order.placed" }, "fulfilment.inbound"},
{func(e Envelope) bool { return e.Type == "orders.order.canceled" }, "refunds.inbound"},
}
func route(ctx context.Context, e Envelope, msg Message) error {
for _, r := range routes {
if r.Match(e) { return publish(ctx, r.Topic, msg) }
}
// ★★ 沒有匹配 —— 絕對不要靜默 return nil
metrics.Unroutable.WithLabelValues(e.Type).Inc()
return publish(ctx, invalidMessageChannel, msg)
}★★ 「沒有匹配路由」是內容路由器的頭號 bug:
靜默 drop → 訊息人間蒸發,而且沒有任何指標會顯示這件事。
→ 一定要送 無效訊息通道 + 加上帶 type 標籤的計數器。
★★ 先問:能不能不要路由器?
★ 三種「不需要路由器」的替代方案,成本都更低:
① 生產者直接發到正確的通道(★ 它通常知道自己在發什麼)
② ★ 用 broker 的原生路由(RabbitMQ topic exchange、SNS filter policy)—— 零程式碼
③ ★ 讓消費者用 [[選擇性消費者]] 自己過濾 —— 零額外元件
★ 路由器是額外一跳:多一次延遲、多一個部署單元、多一個會掛的元件、
多一段「訊息為什麼沒到」的可能原因。
★ 判準:路由規則是「跨越多個生產者的共同決策」嗎?
不是(單一生產者就知道要發哪裡)→ 不要加路由器。
⚠️ Kafka 幾乎沒有內建路由(見 訊息路由器),所以在 Kafka 上「依內容分流」
就是一個你要自己寫、部署、監控的服務——這讓上面的判準更重要。
★ 路由規則放哪:預設放程式碼
| 放哪 | 什麼時候 |
|---|---|
| ★ 寫在程式碼裡 | 預設選擇。規則是邏輯,就該被 review、被測試、被版控 |
| 設定檔/DB | 營運人員要在不部署的情況下調整。★ 必須配規則的測試與變更審計 |
| broker 拓撲 | 規則很簡單且穩定。★ 但拓撲要由 IaC 宣告,不要手動點 |
★★ 三個實務上的硬要求
① ★ 無狀態
路由器不該記任何東西 → 可以隨意水平擴展、隨意重啟
有狀態的需求(等齊、排序)→ 那是 [[聚合器]]/[[重排序器]],不是路由器
② ★ 只看標頭,不解 body
解 body 才能決定去向 → ★ 路由器被每一種 schema 綁死,schema 一改就要改它
→ 需要的路由依據要「提升到標頭」(見 [[訊息]]、[[信封包裝器]])
③ ★★ 路由器本身要冪等/可重送
路由器發出訊息後、ack 來源前崩潰 → 重送 ⇒ 下游收到兩份
→ ★ 下游必須冪等([[冪等接收者]])。路由器無法自己解決這件事
★ 兄弟辨析(一個輸入、一個輸出 vs 其他)
| 輸出 | 差別 | |
|---|---|---|
| 內容路由器 | 恰好 1 | 依內容選一個目的地 |
| 訊息過濾器 | 0 或 1 | ★ 不符條件就丟掉,沒有「另一個目的地」 |
| 收件人清單 | N | ★ 算出一份清單,全部都送 |
| 分割器 | N | ★ 依內容項目拆成多則(不是複製) |
| 動態路由器 | 1 | ★ 路由表由目的地自己註冊,不是寫死 |
| 路由單 | 1 | ★ 路徑寫在訊息裡,路由器只看下一站 |
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
路由器需知道所有可能目的地與判斷規則,容易變成維護熱點。
-
規則寫死時,新增目的地就得改路由器——可改用動態路由器。
-
只會選一個目的地;若要送給多個,用收件人清單。
-
★★ 「沒有匹配路由」絕對不要靜默丟棄。 送 無效訊息通道 + 加帶 type 標籤的計數器。
-
★★ 先問能不能不要路由器:生產者直接發、broker 原生路由、消費者自己過濾——三者成本都更低。
-
★ 路由器只看標頭,不解 body。解 body 就被每種 schema 綁死。
-
★ 路由器要無狀態,才能水平擴展與隨意重啟。
-
★ 路由器重送會讓下游收到兩份。下游必須冪等,路由器自己解決不了。
-
★ 路由規則預設放程式碼。放設定檔就失去型別檢查,改錯執行期才知道。
-
★ Kafka 幾乎沒有內建路由,這裡的每一個路由器都是一個要維運的服務。
-
路由器是額外一跳:多延遲、多一個會掛的元件、多一種「訊息為什麼沒到」的原因。
🔗 相關工具
- 訊息過濾器 Message Filter(只有一個輸出、且可能不輸出的特例)
- 動態路由器 Dynamic Router(規則可動態註冊、不寫死)
- 收件人清單 Recipient List(可送往多個目的地)
- 企業整合模式
- 訊息路由器 —— 家族總覽與「規則放哪」的完整討論
- 訊息過濾器、收件人清單、分割器 —— 其他輸出型態
- 動態路由器、路由單 —— 路由決策的其他來源
- 選擇性消費者 —— 不加路由器的替代方案
- 無效訊息通道 —— 沒有匹配時的去處
- 信封包裝器 —— 把路由依據提升到標頭