🎯 什麼情境該想到我

當你「訊息要依序經過一連串處理步驟,且順序可事先算好、附在訊息上」的時候。

⚙️ 怎麼用(步驟 / 公式)

意圖:讓訊息依序經過一系列處理步驟,而這些步驟事先未知或可變。

做法要點:

  • 在訊息附上一份「路由單」——一串要依序拜訪的處理站清單。
  • 每一站處理完,就照清單把訊息轉給下一站。
  • 直到清單走完為止。像製造流程單隨著加工件流轉。

★ 核心機制:路徑寫在訊息裡,每個站只看「下一站」

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)。

  • ★★ 沒有地方查「現在到哪了」。訊息在通道之間,不在資料庫裡。要配稽核事件(訊息歷史)。

  • ★ 路徑是訊息裡的資料,不受型別檢查。打錯通道名要走到那一站才爆。起點要驗證白名單。

  • ★★ 外部來源的路徑必須驗證:白名單 + 長度上限 + 不重複(防無限迴圈、防導向敏感通道)。

  • ★ 每一站都必須冪等。重送會讓已完成的站重跑。

  • 只適合線性流程。需要分支、等待多方、人工介入 → 用流程管理器。

  • 站數多了就難以理解。六站以上時,「這則訊息會走哪條路」變成要讀資料才知道。

🔗 相關工具