🎯 什麼情境該想到我
當你「訊息要依序經過一連串處理步驟,且順序可事先算好、附在訊息上」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:讓訊息依序經過一系列處理步驟,而這些步驟事先未知或可變。
做法要點:
- 在訊息附上一份「路由單」——一串要依序拜訪的處理站清單。
- 每一站處理完,就照清單把訊息轉給下一站。
- 直到清單走完為止。像製造流程單隨著加工件流轉。
★ 核心機制:路徑寫在訊息裡,每個站只看「下一站」
type RoutingSlip struct {
Steps []string `json:"steps"` // ★ ["validate", "enrich", "tax", "ship"]
Current int `json:"current"` // ★ 已完成到第幾站
}
// ★ 每個處理站的收尾動作:推進索引、發給下一站
func advance(ctx context.Context, e Envelope) error {
e.Slip.Current++
if e.Slip.Current >= len(e.Slip.Steps) {
return publish(ctx, e.Slip.ReplyTo, e) // ★ 走完了
}
return publish(ctx, e.Slip.Steps[e.Slip.Current], e)
}★ 和 管道與過濾器 的差別:管道的順序是固定的部署拓撲;
路由單的順序每則訊息可以不同,而且由起點決定。★ 和 流程管理器 的差別:路由單沒有中央狀態(狀態在訊息裡),
所以它不能分支、不能等待多方、不能補償——只能線性往前。
★ 什麼時候它比流程管理器好
| 選路由單 | 選 流程管理器 |
|---|---|
| ★ 線性、不分支 | 需要條件分支、迴圈 |
| ★ 站數少(3–6) | 站數多或動態產生 |
| ★ 不需要查詢「現在到哪了」 | ★ 需要查進度、需要人工介入 |
| 不需要補償/回滾 | ★ 需要 Saga 式補償 |
| ★ 不想維護一個有狀態服務 | 已經有了 |
★ 路由單最大的優點:完全無狀態。 沒有中央協調者、沒有狀態儲存、
沒有「協調者掛了怎麼辦」——訊息自己帶著地圖走。
★★ 四個實務問題(每一個都會出事)
① ★★ 訊息會越來越大
每站都想往訊息裡加一點東西(處理結果、稽核紀錄)
→ ★ 走到第六站時訊息可能是原本的十倍,撞上 broker 大小上限
→ ★ 對策:中間結果寫外部儲存,訊息只帶 key([[寄物單]])
② ★★ 中途失敗沒有回滾機制
第 4 站失敗 → 前 3 站的副作用已經發生了
→ ★ 路由單本身無法補償。要補償就必須升級成 [[流程管理器]](Saga)
→ ★ 務實解:讓每一站的動作都可重試且冪等,失敗就重試到成功或進 DLQ
③ ★★ 沒有地方查「現在到哪了」
訊息在通道之間,不在任何資料庫裡
→ ★ 「這筆訂單卡在哪」沒有答案,只能翻日誌
→ ★ 對策:每站發一則「已完成第 N 站」的事件到稽核通道([[訊息歷史]])
④ ★ 路徑是訊息裡的資料 ⇒ 不受型別檢查
Steps 裡有一個打錯字的通道名 → ★ 走到那一站才爆,而且訊息可能卡在中間
→ ★ 對策:起點驗證所有 step 都在白名單內(同 [[回覆位址]] 的問題)
★ 安全:路徑不可信任
// ★★ 若訊息來自外部或不可信的服務,Steps 必須驗證
for _, s := range slip.Steps {
if !allowedSteps[s] {
return publishTo(invalidChannel, e, fmt.Errorf("不允許的步驟 %q", s))
}
}⚠️ 可被濫用的三種方式:導向敏感通道、製造無限迴圈(同一站重複出現)、
指向不存在的通道(Kafka 可能自動建 topic)。★ 要驗證白名單 + 長度上限 + 不重複。
★ 兄弟辨析
| 差別 | |
|---|---|
| 管道與過濾器 | ★ 順序是固定拓撲;路由單每則訊息可不同 |
| 流程管理器 | ★ 有中央狀態,可分支、可等待、可補償 |
| 動態路由器 | ★ 中央路由表;路由單的「表」在訊息裡 |
| 訊息歷史 | ★ 記錄「已經過了哪些站」(往回看);路由單記錄「還要去哪」(往前看) |
| 責任鏈模式 | 行程內的同一想法(★ 但責任鏈通常是「誰能處理就處理」,會中斷) |
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
適合線性、順序固定的流程。
-
有分支、並行、合流等複雜流程時,應改用流程管理器。
-
路由單需正確地維護與隨訊息傳遞。
-
★★ 訊息會越走越大。每站都想加東西,第六站可能是原本的十倍,撞上 broker 上限。中間結果用 寄物單。
-
★★ 路由單無法補償。第 4 站失敗,前 3 站的副作用已發生。要補償就必須升級成 流程管理器(Saga)。
-
★★ 沒有地方查「現在到哪了」。訊息在通道之間,不在資料庫裡。要配稽核事件(訊息歷史)。
-
★ 路徑是訊息裡的資料,不受型別檢查。打錯通道名要走到那一站才爆。起點要驗證白名單。
-
★★ 外部來源的路徑必須驗證:白名單 + 長度上限 + 不重複(防無限迴圈、防導向敏感通道)。
-
★ 每一站都必須冪等。重送會讓已完成的站重跑。
-
只適合線性流程。需要分支、等待多方、人工介入 → 用流程管理器。
-
站數多了就難以理解。六站以上時,「這則訊息會走哪條路」變成要讀資料才知道。
🔗 相關工具
- 流程管理器 Process Manager(更靈活但集中化,處理分支與並行)
- 內容路由器 Content-Based Router(每一站也可能再依內容路由)
- 企業整合模式
- 管道與過濾器 —— 固定拓撲的版本
- 流程管理器 —— ★ 需要分支/等待/補償時
- 動態路由器 —— 中央路由表
- 訊息歷史 —— 往回看的紀錄
- 寄物單 —— 中間結果太大時
- 責任鏈模式 —— 行程內的同一想法