🎯 什麼情境該想到我
當你「想依某些規則把訊息導向不同的接收者/通道,又不想讓發送方知道這些接收者是誰」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:用一個專責元件,依規則把訊息從輸入通道轉送到正確的輸出通道,但不改變訊息內容。
做法 / 結構要點:
- 路由器從輸入通道取訊息,依判斷條件(訊息內容、標頭、系統狀態等)選一個輸出通道送出。
- 它只決定「去哪」,不動訊息本體。
- 把路由邏輯集中在一處,發送方與接收方彼此解耦,可獨立增減接收方。
★★ 最重要的一個設計決定:路由規則放在哪
這是整個路由家族真正的難題,EIP 書裡談得很少。
| 放哪 | 優點 | ★ 代價 |
|---|---|---|
| ① 寫死在路由器程式碼裡 | 簡單、可測、可版控 | 改規則要重新部署 |
| ② 設定檔/資料庫 | 不用重新部署 | ★ 規則變成沒有型別檢查的資料,改錯了執行期才知道 |
| ③ broker 的路由能力 | 零程式碼 | ★ 拓撲變成基礎設施設定,不在 git 裡,沒人看得懂 |
| ④ ★ 不要路由器:讓消費者自己過濾 | 最簡單 | 每個消費者都收到全部訊息(量大時浪費) |
★ 判準:這個規則多久改一次?
很少改(一季一次)→ ①,寫在程式碼裡。規則是邏輯,就該像邏輯一樣被 review 和測試。
經常改(營運人員要調)→ ② 或 ③,但必須配上「規則的測試」與「規則變更的審計」。
⚠️ 最常見的失敗:選了 ③,然後兩年後沒有人知道訊息為什麼會流到那裡。
RabbitMQ 的 exchange/binding 拓撲特別容易變成這樣——它不在任何 repo 裡。
→ 至少要讓拓撲由程式碼宣告(IaC/啟動時 declare),見 隨選環境與基礎設施即程式碼。
★ 各家 broker 內建的路由能力(很多時候你不用自己寫)
| broker | 內建路由 |
|---|---|
| RabbitMQ | ★ 最強:direct(精確 routing key)/topic(order.*.created 通配)/fanout/headers exchange |
| SNS | ★ 訂閱篩選政策(filter policy)依訊息屬性過濾 |
| Kafka | ★ 幾乎沒有——分區由 key 決定,跨 topic 路由要靠 Kafka Streams 或自己寫消費者 |
| NATS | 主題通配(orders.*.placed、orders.>) |
★ Kafka 沒有內建路由,是選型時最常被低估的差異。
「依內容分流到不同 topic」在 Kafka 裡就是一個你要自己寫、自己部署、自己監控的服務。
★ Go 的實作:路由器就是一個消費者 + 一個生產者
// ★ 路由器必須是無狀態的 —— 這樣才能水平擴展、隨意重啟
func route(msg Envelope) (topic string, err error) {
switch msg.Type { // ★ 只看標頭,不解 body(見 [[訊息]])
case "orders.order.placed": return "fulfilment.inbound", nil
case "orders.order.canceled": return "refunds.inbound", nil
}
return "", fmt.Errorf("no route for type %q", msg.Type) // ★ 一定要處理「沒有路由」
}
// ★★ 沒有匹配的規則時,絕對不要靜默丟棄
if topic, err := route(msg); err != nil {
return publish(ctx, invalidMessageChannel, msg) // ★ 見 [[無效訊息通道]]
}★★ 「沒有匹配路由」的處理是路由器最常見的 bug:
靜默 drop → 訊息人間蒸發,而且沒有任何指標會顯示這件事。
一定要送 無效訊息通道 並加計數器告警。
★ 兄弟辨析:整個路由家族一次看懂
| 模式 | 輸入 → 輸出 | 有狀態? | 一句話 |
|---|---|---|---|
| 內容路由器 | 1 → 1 | 無 | 依內容選一個目的地 |
| 訊息過濾器 | 1 → 0 或 1 | 無 | 不符條件就丟掉 |
| 收件人清單 | 1 → N | 無 | 依規則算出一份清單,全部送 |
| 分割器 | 1 → N | 無 | 依內容項目拆成多則 |
| 聚合器 | N → 1 | ★ 有 | 收齊後合併 |
| 重排序器 | N → N | ★ 有 | 只排序,不合併 |
| 動態路由器 | 1 → 1 | ★ 有(路由表) | 目的地自己來註冊 |
| 路由單 | 1 → 1 | 無(狀態在訊息裡) | ★ 路徑寫在訊息上 |
| 流程管理器 | 1 → N(多回合) | ★ 有 | 中央協調,可分支、可等待 |
★ 有狀態的三個(聚合器、重排序器、流程管理器)是這個家族真正困難的部分——
它們需要處理逾時、記憶體上限、重啟後的狀態恢復。無狀態的那些幾乎都是十行程式碼。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
路由器是有狀態或需維護規則的元件,可能成為瓶頸或單點;規則過多會難維護。
-
若只是要改資料格式而非分流,那是轉換器的工作,不要混在一起。
-
★★ 「沒有匹配路由」絕對不要靜默丟棄。要送 無效訊息通道 並加計數器告警,否則訊息會人間蒸發。
-
★★ 路由規則放在 broker 設定裡,兩年後沒人看得懂。至少讓拓撲由程式碼宣告。
-
★ Kafka 幾乎沒有內建路由。「依內容分流」在 Kafka 是一個你要自己寫、部署、監控的服務。
-
★ 路由器不該解開 body。只看標頭,否則 schema 一改就要改路由器。
-
路由器是額外一跳:多一次延遲、多一個會掛的元件。能讓消費者自己過濾時就不要加路由器。
-
規則放設定檔就失去型別檢查。改錯了執行期才知道 → 要有規則的測試。
🔗 相關工具
- 管道與過濾器 Pipes and Filters(路由器是其中一種過濾器)
- 訊息轉換器 Message Translator(互補:路由器決定去向、轉換器改變內容)
- 訊息通道 Message Channel(路由器分流的來源與去向)
- 企業整合模式
- 內容路由器、訊息過濾器、收件人清單、動態路由器 —— 無狀態路由
- 分割器、聚合器、重排序器 —— 拆分與收斂
- 路由單、流程管理器 —— 路徑可變/需要協調
- 無效訊息通道 —— 沒有匹配路由時的去處
- 訊息 —— 為什麼路由只看標頭
- 隨選環境與基礎設施即程式碼 —— 讓 broker 拓撲進版控