🎯 什麼情境該想到我

當你「有兩套不同的訊息系統(不同廠牌/平台),需要讓訊息在它們之間流通」的時候。

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

意圖:用一個橋接元件連接兩套訊息系統,把一邊通道上的訊息複製/轉送到另一邊的通道。

做法 / 結構要點:

  • 橋接同時是兩套訊息系統的端點:從系統 A 的通道取訊息,送到系統 B 的對應通道(反向亦然)。
  • 過程中依需要做必要的格式或協定轉換,讓兩邊都能理解。
  • 應用程式各自仍只面對自己熟悉的訊息系統,跨系統的細節由橋接吸收。

★ 什麼時候真的需要橋接(四個實際場景)

場景例子
① ★ 併購/整合兩間公司各用 Kafka 與 RabbitMQ,短期內都不能換
② ★ 跨地域/跨叢集複製Kafka MirrorMaker 2、Confluent Cluster Linking、RabbitMQ Shovel/Federation
③ ★ 遷移期間並行從 RabbitMQ 遷到 Kafka,橋接讓兩邊同時有資料,可逐服務切換
④ 雲端/地端混合地端 broker ↔ 雲端託管服務

★ 場景 ③ 是最常見也最有價值的用法:
橋接讓「換 broker」變成可以一個服務一個服務切換,而不是一次性大爆炸。

★★ 橋接必然遇到的四個語義落差

不同 broker 的保證不一樣,橋接不可能無損:

① ★★ 順序
   RabbitMQ 的 queue(單一消費者)有序 → Kafka 需要分區鍵才有序
   ★ 橋接程式必須決定「用哪個欄位當 partition key」—— 選錯就沒有順序保證

② ★★ 投遞保證變成「至少一次」的疊加
   來源 ack → 目標發送失敗 → 訊息丟了
   目標發送成功 → 來源 ack 失敗 → ★ 重送 ⇒ 重複
   ★ 橋接本身就是一次雙寫問題,無法做到恰好一次
   → ★ 正確順序:先成功發到目標,再 ack 來源(寧可重複,不要遺失)

③ ★ 標頭與 metadata
   AMQP 的 properties ↔ Kafka 的 headers ↔ SQS 的 message attributes
   ★ 型別與大小限制都不同,對應不完整就會靜默掉欄位
   → ★ correlation-id / trace-parent 一定要手動確保有帶過去

④ ★ 大小限制
   來源允許 10 MB、目標上限 1 MB → ★ 橋接處才發現,而且無處可退
   → 要嘛前置用 [[寄物單]],要嘛橋接把超大訊息送進 [[無效訊息通道]]

★ Go 的橋接骨架

func bridge(ctx context.Context, src Source, dst Sink) error {
    for {
        msg, err := src.Fetch(ctx)
        if err != nil { return fmt.Errorf("fetch: %w", err) }
 
        out, err := translate(msg)                  // ★ 純函式,可測(見 [[訊息轉換器]])
        if err != nil {
            _ = dst.PublishTo(ctx, invalidChannel, msg, err)
            _ = msg.Ack()                            // ★ 壞訊息要 ack 掉,否則堵住
            continue
        }
 
        if err := dst.Publish(ctx, out); err != nil {
            _ = msg.Nack(true)                       // ★ 目標失敗 → 不 ack,讓來源重送
            continue
        }
        _ = msg.Ack()                                // ★★ 一定在目標成功「之後」
    }
}

★ 這段的三個關鍵順序:翻譯失敗 → 搬走並 ack;目標失敗 → 不 ack;成功 → 才 ack。
任一個順序寫反都會造成遺失或無限堵塞。

★★ 最重要的警告:橋接很容易變成永久的

「我們先用橋接,遷移完就拆掉」
→ 三年後橋接還在,而且:
  ★ 兩套 broker 都要維運、都要值班
  ★ 訊息路徑變成「A → 橋接 → B」,除錯要跨兩個系統追
  ★ 橋接是單點,掛了沒有任何錯誤出現(見 [[通道配接器]] 的同一個問題)
  ★ 沒人記得它為什麼存在

★ 對策:橋接上線時就設一個明確的退場條件與日期,寫進 ticket。
沒有退場計畫的橋接,就是永久增加一倍的維運面積。

★ 兄弟辨析

差別
通道配接器★ 連接「非訊息系統 ↔ 訊息系統」(DB、API、檔案)
訊息匯流排架構層級:一組共用通道 + 標準格式,★ 不是點對點的轉接
訊息轉換器橋接內部用到它(格式轉換)
標準資料模型★ 橋接多了以後,就會有人提議用它——注意它的代價

🧪 我實際套用的紀錄

  • (待填)

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

  • 橋接需為每對要連通的通道設定與維護,通道多時管理成本高。

  • 與通道配接器不同:橋接連的是「兩套訊息系統」,配接器連的是「非訊息的系統」。

  • ★★ 橋接本身就是雙寫問題,做不到恰好一次。 正確順序是「先成功發到目標,再 ack 來源」——寧可重複,不要遺失。

  • ★★ 「遷移完就拆掉」的橋接通常會變成永久的。 上線時就要有明確的退場條件與日期。

  • ★ 順序保證無法自動搬過去。RabbitMQ 的 queue 順序 → Kafka 需要選對 partition key。

  • ★ 標頭對應不完整會靜默掉欄位。correlation-id/trace-parent 要手動確保帶過去。

  • ★ 大小限制不同,超限在橋接處才爆而且無處可退。 前置用 寄物單 或送 無效訊息通道。

  • ★ 橋接是單點,掛了不會有任何錯誤出現。 要監控「最後一筆搬運的時間」與兩邊的 lag。

  • 橋接讓除錯要跨兩個系統追。trace id 必須貫穿。

  • 不要用橋接掩蓋「其實該統一 broker」。兩套 broker 的維運成本是加倍的。

🔗 相關工具