🎯 什麼情境該想到我

當你「想主動、定期地確認訊息處理元件仍運作正常,而不是等真的出錯才發現」的時候。

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

意圖:定期注入內容已知的測試訊息,並在輸出端驗證結果是否符合預期,藉此主動監測元件是否正常運作。

做法 / 結構要點:

  • 由測試資料產生器發出可辨識的測試訊息,注入待測元件。
  • 讓測試訊息與正式訊息一起流經處理。
  • 在輸出端用測試訊息分離器把測試訊息挑出,驗證器比對結果,異常則告警。

★★ 它要抓的是「其他監控抓不到」的那一類故障

★ 傳統監控看得到的:
  ✅ 行程掛了(liveness)      ✅ CPU/記憶體異常
  ✅ 佇列積壓(lag)           ✅ 錯誤率上升

★★ 傳統監控看不到的 —— 這才是測試訊息的目標:
  ★ 服務活著、沒有錯誤,但「靜默地產生錯誤的結果」
  ★ 服務活著、沒有積壓,但「根本沒有訊息進來」(★ 上游停了)
  ★ 整條管線的某一段悄悄不轉了(配接器掛了,見 [[通道配接器]])

★★ 最重要的一個場景:「沒有訊息」這件事不會觸發任何告警。
上游停止發送 → lag = 0、錯誤率 = 0、CPU 正常 → 所有儀表板都是綠的。
★ 測試訊息(合成監控)是唯一能抓到這件事的手段。
⚠️ 另一個更便宜的做法:對「最後一筆真實訊息的時間」告警——
流量穩定的通道用這個就夠了,不必注入測試訊息。

★★ 設計的四個必要決定

① ★★ 測試訊息怎麼被識別(★ 最重要)
   ✅ 信封裡一個明確的欄位:is_synthetic: true
   ❌ 靠特殊的 ID 格式("TEST-001")—— ★ 太容易被誤判或誤用

② ★★ 誰負責把它濾掉
   → 每個消費者都要記得濾 = ★ 一定有人忘記 ⇒ ★★ 測試訊息造成真實的副作用
   → ✅ ★ 做進共用的消費端閘道(見 [[訊息閘道]]),預設就濾掉

③ ★ 它走不走真實路徑
   走真實路徑 → ★ 測得更真,但★★ 有副作用風險
   走影子路徑 → 安全,但★ 測不到真實路徑的問題
   → ★ 折衷:走真實路徑,但在「最後一步(產生副作用之前)」被攔下

④ ★ 誰驗證它到了
   → ★ 一個獨立的驗證器訂閱終點通道,比對「發出的」與「收到的」
   → ★★ 沒有驗證器的測試訊息 = 沒有意義(你發了但沒人確認它到了)

★★ Go 的實作:濾掉測試訊息必須是「預設行為」

// ★★ 在共用閘道裡濾掉,而不是每個 handler 自己判斷
func (a *Activator) Handle(ctx context.Context, msg Message) error {
    e, err := decode(msg)
    if err != nil { return err }
 
    if e.IsSynthetic {
        if !a.acceptsSynthetic {           // ★★ 預設 false —— 選擇加入,不是選擇退出
            metrics.SyntheticSkipped.Inc()
            return msg.Ack()                // ★ ack 掉,讓它不堵住
        }
        ctx = withSyntheticFlag(ctx)        // ★ 讓下游知道「不要真的發錢」
    }
    return a.handler.Handle(ctx, e)
}

★★ 「預設不接受、明確選擇加入」是唯一安全的設計:
反過來(預設處理、要自己濾掉)→ 只要有一個服務忘記濾,測試訊息就造成真實副作用
(真的寄信、真的扣款)。

★ 這正是「安全的預設值」原則:讓「忘記做某件事」的後果是保守的,不是危險的。

★★ 最危險的失敗模式:測試訊息造成真實副作用

★ 真實發生過的樣貌:
  ① 測試訂單流進真實的出貨系統 ⇒ ★ 真的出貨了
  ② 測試付款訊息進真實的金流 ⇒ ★★ 真的扣款
  ③ 測試訊息進了報表 ⇒ ★ 營收數字錯了(而且很難發現)
  ④ ★ 測試訊息進 DLQ ⇒ 淹沒真正的失敗

★ 四個防護,缺一不可:
① 預設不接受(見上);② 產生副作用的最後一步再攔一次;
③ ★ 測試訊息一律用明顯的測試資料(TEST_ 前綴的 ID、0 元金額);
④ ★ 報表與指標明確排除 is_synthetic。

★ 什麼時候不需要測試訊息

  • ★ 流量穩定的通道 → 對「最後一筆真實訊息的時間」告警就夠了,更便宜也更安全。
  • ★ 已經有端到端的合成監控(從 API 打進去、驗證最終結果)→ 那是更完整的版本。
  • ★ 有副作用且無法安全攔下的流程(實體出貨、金流)→ 風險大於收益,改用被動監控。

★ 兄弟辨析

差別
控制匯流排★ 測試訊息常透過它注入與收集結果
竊聽器觀察真實訊息,不注入
繞道★ 導流真實訊息;測試訊息是注入新的
工具-注入故障與韌性演練★ 更主動的版本:不只探測,還製造故障

🧪 我實際套用的紀錄

  • (待填)

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

  • 要能把測試訊息與正式訊息區分,避免污染正式資料或下游。

  • 測試涵蓋的路徑要有代表性。

  • 常由控制匯流排協調注入與收集結果。

  • ★★ 最重要的目標是「沒有訊息」這種故障——lag=0、錯誤率=0、儀表板全綠,但上游停了。

  • ★★ 但更便宜的做法常常是「對最後一筆真實訊息的時間告警」。流量穩定的通道用這個就夠,不必注入。

  • ★★ 濾掉測試訊息必須是預設行為(預設不接受、明確選擇加入)。 反過來只要有一個服務忘記濾就造成真實副作用。

  • ★★ 測試訊息造成真實扣款/出貨是真實發生過的事故。 要四層防護:預設不接受、副作用前再攔、明顯的測試資料、報表排除。

  • ★ 靠 ID 格式(TEST-001)識別太脆弱。用信封裡明確的 is_synthetic 欄位。

  • ★★ 沒有驗證器的測試訊息沒有意義——你發了但沒人確認它到了。

  • ★ 測試訊息進 DLQ 會淹沒真正的失敗。要在 DLQ 指標裡分開計數。

  • 有副作用且無法安全攔下的流程(實體出貨、金流)不要用這個模式,風險大於收益。

🔗 相關工具