🎯 什麼情境該想到我

當你要用訊息佇列/事件把多個系統串接起來,思考「訊息怎麼流、怎麼繞送、怎麼轉換」時。

⚙️ 怎麼用(選對模式組合)

  1. 通道(Channel):訊息的傳輸管道;點對點(一對一消費)vs 發布訂閱(一對多)。
  2. 路由(Routing):
    • Router 依內容決定去向、Filter 過濾、Splitter 拆分、Aggregator 聚合多筆為一。
  3. 轉換(Transformation):Translator 轉格式、Enricher 補資料——讓不同系統的訊息互通。
  4. 端點(Endpoint):系統如何收發;冪等消費者處理重複投遞(見 工具-MQ消費端防禦三原則)。
  5. 可靠性:保證投遞 + 死信佇列 + 重試退避。

★ 先分清楚:命令 vs 事件 vs 文件訊息

類型語義接收者可以拒絕嗎命名
命令 Command「請做這件事」★ 恰好一個✅祈使句:CreateOrder
★ 事件 Event「這件事已經發生了」★ 零到多個❌ 已經發生了過去式:OrderPlaced
文件 Document「這是資料」任意—名詞

★★ 混用這三者的語義,是事件驅動架構最常見也最致命的設計錯誤。
把 SendEmail(命令)發成事件 → 兩個服務都訂閱了 → 寄了兩封信。
把 OrderPlaced(事件)當命令 → 發布者開始關心「誰處理了、處理成功沒」→ 耦合回來了。

判準:發布者在不在乎「有沒有人處理」? 在乎 → 命令;不在乎 → 事件。

★ 投遞保證:三種語義

保證意思實務
至多一次可能丟失遙測、指標
★ 至少一次可能重複★ 幾乎所有 MQ 的實際保證
恰好一次不丟不重★ 端到端幾乎不存在

★★ 「恰好一次」在分散式系統裡基本上是行銷用語。
Kafka 的 exactly-once 只在「Kafka → 處理 → Kafka」的封閉迴圈內成立;
一旦你的處理有外部副作用(呼叫 API、寄信、寫別的資料庫),就回到「至少一次 + 冪等」。

所以:接受「至少一次」,把冪等做好。 這是唯一務實的路線(見 工具-MQ消費端防禦三原則)。

冪等的三種做法:

① ★ 訊息帶唯一 ID,消費端記錄「處理過的 ID」(去重表 + 唯一索引)
② ★ 讓操作天然冪等:SET 而非 INCREMENT、UPSERT 而非 INSERT
③ 版本號/條件更新:WHERE version = ?(見 [[工具-交易與隔離等級]])

★ 順序保證:代價比想像中大

★ 要保證順序 → 同一個 key 的訊息必須進同一個分區,且該分區只能有一個消費者
  → ★ 那個 key 的處理就無法並行
  → 吞吐量被單一消費者限制

★ 判準:真的需要全域順序嗎? 多數情況只需要**「同一個實體的順序」**
(同一個訂單的事件要有序,不同訂單之間無所謂)→ 用實體 ID 當分區鍵(見 工具-資料分區)。

更好的做法:讓處理與順序無關

訊息帶版本號或時間戳,消費端丟棄比已處理的更舊的訊息。
★ 這樣即使亂序也不會出錯,而且可以並行處理。

★ 死信佇列(DLQ):一定要有,但更要有處理流程

訊息處理失敗 → 重試 N 次(指數退避)→ 仍失敗 → ★ 送進 DLQ
沒有 DLQ 會怎樣有 DLQ 但沒流程會怎樣
★ 毒訊息(poison message)卡住整個佇列 —— 一直重試同一筆,後面全部堵住★ DLQ 累積幾萬筆沒人看,等於資料默默遺失

★ DLQ 必須配三件事:

  1. 告警(有訊息進 DLQ 就通知)
  2. 可檢視(能看到訊息內容與失敗原因)
  3. ★ 可重放(修好之後能把它們重新送回主佇列)

★ 常用的路由模式(EIP 的核心幾個)

模式做什麼對應
內容路由器依訊息內容決定去向內容路由器
訊息過濾器不符條件就丟棄訊息過濾器
分割器 Splitter一則拆成多則(訂單 → 每個品項一則)分割器
★ 聚合器 Aggregator多則合成一則。★ 最難的一個:要決定「等多久」「等幾則」「逾時怎麼辦」聚合器
散發-聚集發給多個處理者再收集結果散發-聚集
★ 收件人清單發給一份動態決定的清單收件人清單

★ 完整的 65 條型錄見 reference/企業整合模式/。

★ 什麼時候不該用訊息佇列

情況用什麼
需要立即的回應同步 HTTP/gRPC
★ 只有兩個服務、呼叫量小直接呼叫——MQ 是一整套要維運的基礎設施
需要強一致交易(見 工具-分散式事務的取捨)
只是想「解耦」但沒有實際的非同步需求★ 先問解耦要解決什麼問題

🧪 我實際套用的紀錄

  • 2026-07-15:(待填)

⚠️ 注意 / 什麼時候不適用

  • ★★ 消費端冪等不是可選的。所有實務上的 MQ 都是「至少一次」。
  • ★ 命令與事件的語義不要混用(見上)。這會在系統長大後變成難以拆解的耦合。
  • ★ DLQ 要有處理流程,否則它只是「資料消失的地方」。
  • 訊息佇列會隱藏因果鏈。「為什麼這筆訂單沒出貨」要追過好幾個佇列——
    ★ 訊息一定要帶 correlation/trace id(見 工具-遙測與監控)。
  • ★ 佇列積壓要告警。積壓代表消費者跟不上——而且延遲會持續惡化直到消費者追上或佇列爆掉。
  • 訊息的 schema 也要版本管理。消費者可能是舊版(見 工具-資料編碼與演進)。
  • 不要把 MQ 當資料庫。它是傳輸不是儲存;需要查詢歷史就寫進資料庫。
  • ★ 非同步讓錯誤處理變難:呼叫端已經回應使用者了,後續失敗要怎麼通知?這要在產品層面設計
    (見 工具-分散式事務的取捨 的產品配合)。

🔗 相關工具