🎯 什麼情境該想到我
當你「有一個需要多個步驟的處理流程,想把每步拆成獨立、可重組、可重用的小元件」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:把一個複雜的處理,拆成一連串各自獨立的處理步驟(過濾器),用通道(管道)串接起來。
做法 / 結構要點:
- 每個 過濾器(Filter) 只負責一件事,從輸入通道收訊息、處理後寫入輸出通道。
- 管道(Pipe) 就是連接兩個過濾器的訊息通道。
- 過濾器彼此只透過管道相連、互不知情,因此可自由增減、重排、替換、平行化。
★★ 這個模式的核心紀律:每個過濾器只認識「通道」,不認識彼此
Order ──▶ [驗證] ──▶ [補資料] ──▶ [計稅] ──▶ [出貨]
↑ 每一段都是「同一種介面:吃訊息、吐訊息」
★ 所以你可以任意重排、插入、移除一段,而不必改其他段。
這正是 裝飾者模式/middleware 在行程內的同一個想法——
★ 差別只在「邊界是函式呼叫還是訊息通道」。
★ Go 的三種實作,成本差三個數量級
// ① ★ 行程內:函式組合(middleware)—— 零基礎設施成本
type Filter func(context.Context, Order) (Order, error)
func Chain(fs ...Filter) Filter {
return func(ctx context.Context, o Order) (Order, error) {
var err error
for _, f := range fs {
if o, err = f(ctx, o); err != nil { return o, err } // ★ 任一段失敗就停
}
return o, nil
}
}
pipeline := Chain(validate, enrich, calcTax)
// ② 行程內、非同步:goroutine + channel
// ★ 每段一個 goroutine,用 channel 串起來 —— 可以各自並行
// ⚠️ 但行程死了資料就沒了,而且沒有重試(見 [[工具-靠通訊共享記憶體]])
// ③ ★ 跨行程:每段一個服務,用真的 MQ 串
// ★ 代價:N 個部署單元、N 個佇列要監控、除錯要跨服務追 trace★★ 判準:這些階段需要「各自獨立擴展」或「各自獨立失敗」嗎?
不需要 → 選 ①。一個函式鏈就是管道與過濾器,而且好測十倍。
需要(某一段特別慢、特別容易掛、要用不同語言寫)→ 才升級到 ③。
⚠️ 最常見的過度設計:把五個純計算步驟拆成五個服務、四個佇列。
那不是「管道與過濾器」的勝利,是把一個函式鏈換成一個分散式系統。
★ 跨行程管道的三個真實代價
① ★ 延遲相加,而且每一跳都有排隊
五段 × (網路 + 排隊 + 處理) → p99 會比單體高一個數量級
② ★★ 中間狀態沒有交易性
第 3 段成功、第 4 段失敗 → 訊息重送會讓第 3 段重跑
→ ★ 每一段都必須冪等([[冪等接收者]])
③ ★ 除錯需要端到端追蹤
「這筆為什麼停在第 3 段」→ 沒有 correlation id 就查不出來
→ ★ trace id 必須貫穿所有段([[訊息歷史]]、[[工具-遙測與監控]])
★ 兄弟辨析
| 差別 | |
|---|---|
| 路由單 | ★ 路徑寫在訊息裡(每則訊息可以走不同順序);管道的順序是固定的拓撲 |
| 流程管理器 | ★ 有一個中央協調者持有狀態,可以分支、等待、補償;管道是線性無狀態的 |
| 組合訊息處理器 | 拆開 → 各自處理 → 合回;管道不拆分 |
| 以管道取代迴圈 | 同一想法在單一函式內的版本(⚠️ 在 Go 裡多半不該做) |
| 拆分階段 | 同一想法在單一函式內、且只切兩段並產生中間型別 |
★ 什麼時候不要用
- ★ 步驟之間需要看到彼此的中間狀態(第 4 段要用第 1 段的原始輸入)→ 那不是管道,
硬做會讓訊息帶著一路累積的原始輸入欄位。 - ★ 需要分支/條件路徑/等待多方 → 流程管理器。
- 只有兩段,而且不會變 → 直接一個函式。
- 需要整體原子性 → 一個資料庫交易。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
步驟切太細會增加通道與序列化開銷,並讓整體延遲與除錯變複雜。
-
這是本書許多路由/轉換模式共用的底層架構觀點,而非具體某個元件。
-
★★ 「管道與過濾器」不代表要拆成多個服務。行程內的函式鏈就是這個模式,而且好測十倍。
-
★ 跨行程版的代價:延遲相加、中間狀態無交易性、除錯要跨服務追 trace。
-
★ 每一段都必須冪等。第 4 段失敗導致重送時,前面幾段會重跑。
-
★ 步驟之間需要看到彼此的中間狀態 → 切點錯了。訊息會長出一路累積的欄位。
-
需要分支、等待、補償 → 用 流程管理器,不要硬塞進線性管道。
-
★ 過濾器的順序是隱性契約。「補資料必須在計稅之前」如果只存在於部署設定裡,遲早會被人改錯——寫進文件與測試。
-
中間通道要監控積壓。管道裡任一段變慢,前面的通道就會堆積。
🔗 相關工具
- 訊息路由器 Message Router(過濾器的一種:依規則決定訊息去向)
- 訊息轉換器 Message Translator(過濾器的一種:改變訊息格式)
- 訊息通道 Message Channel(作為串接過濾器的管道)
- 企業整合模式
- 訊息路由器、訊息轉換器 —— 過濾器的兩種典型職責
- 路由單、流程管理器 —— 路徑可變/需要協調時
- 組合訊息處理器 —— 拆開再合回的變體
- 裝飾者模式 —— 行程內的同一個想法
- 以管道取代迴圈、拆分階段 —— 單一函式內的版本
- 冪等接收者 —— 跨行程管道的前提
- 工具-靠通訊共享記憶體 —— goroutine + channel 版的管道