🎯 什麼情境該想到我
當你「訊息本體要在傳輸途中附帶路由、安全、關聯等額外資訊,又不想污染原始內容,希望進出系統時能一致地包上與拆掉」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:把原始訊息內容包進一個標準「信封」,信封攜帶訊息系統或中介需要的資訊(如路由位址、安全憑證、關聯 ID),內容本體維持原樣。
做法 / 結構要點:
- 進入訊息系統前,用包裝器把應用資料放進信封的內容區,並填入信封標頭欄位。
- 訊息在系統中依信封標頭被路由、驗證、追蹤。
- 抵達目標端點前,由對應的拆封器移除信封、還原原始內容交給應用。
★ 信封的三層與各自的責任
{
"message_id": "01J...", // ★ 冪等去重([[冪等接收者]])
"correlation_id": "ord-42", // ★ 串起一次流程([[關聯識別碼]])
"causation_id": "01J...", // ★ 我被哪一則引起
"type": "orders.order.placed", // ★ 路由依據,不必解 data
"schema_version": 2, // ★ [[格式指示器]]
"occurred_at": "2026-09-25T10:00:00Z",// ★ 業務時間 ≠ 送出時間
"traceparent": "00-4bf9...-01", // ★ W3C 標準,OTel 認得
"data": { ... } // ★ 業務內容,對信封不透明
}★★ 最重要的一條原則:路由與運維資訊在信封,業務資料在
data。
違反它的代價很具體:路由器必須解開data才能決定去向 ⇒ 每個 schema 改一次
就要改路由器(見 訊息路由器)。
★ Go 的實作:延遲解析是關鍵
type Envelope struct {
MessageID string `json:"message_id"`
CorrelationID string `json:"correlation_id"`
Type string `json:"type"`
SchemaVersion int `json:"schema_version"`
OccurredAt time.Time `json:"occurred_at"`
Data json.RawMessage `json:"data"` // ★★ 不立刻解
}★ json.RawMessage 讓路由器、稽核、DLQ 處理器都能處理任何型別的訊息,
而完全不必認識 data 的結構——它們只讀信封。
只有最終的業務處理器才依 Type 與 SchemaVersion 解開 data。
★★ 標頭欄位 vs 信封欄位:一個常被忽略的選擇
| 放 broker 的標頭 | 放 JSON 信封裡 |
|---|---|
| ★ broker 可以直接用來路由/過濾(RabbitMQ headers exchange、SNS filter policy) | broker 看不到 |
| ★ 不必反序列化整則訊息就能讀 | 要先解 JSON |
| ★ 受 broker 的型別與大小限制 | 無限制 |
| ★★ 跨 broker 搬運時容易掉(見 訊息橋接) | ★ 跟著 body 走,不會掉 |
★ 務實做法:兩邊都放。
信封裡是真相(跨 broker 不會掉),標頭是「給基礎設施用的副本」(路由、過濾、取樣)。
⚠️ 但要有一個明確的規則:不一致時以信封為準,否則除錯時你不知道該信哪個。
★★ 這件事必須在「第一天」就做
★ 事後加信封極貴:
① 所有生產者都要改(★ 而且過渡期必須同時支援有/無信封兩種格式)
② 所有消費者都要改解析邏輯
③ ★ 已經在通道裡、已經落地的歷史訊息沒有信封
→ ★★ 實務做法:把信封做進共用的 client library,讓開發者「不必想起」它
(見 [[訊息匯流排]] 的共用 library 討論)
★ 什麼時候不需要信封
- ★ protobuf/Avro + schema registry:★ 格式指示器已經在 wire format 裡
(Confluent 的 magic byte + schema ID),而標頭走 broker header
→ 這時「JSON 信封」是多餘的一層。 - ★ CloudEvents:如果你用它,信封已經被標準化了——
不要自己再發明一套(它定義了id/source/type/time/data等欄位)。 - 內部單一通道、單一生產者與消費者、一起部署。
★ 兄弟辨析
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
包 / 拆兩端要對信封格式有一致約定,否則無法互通。
-
多層包裝會增加處理成本與複雜度。
-
信封只該放傳輸 / 管理用的中介資訊,別把業務邏輯塞進來。
-
★★ 路由與運維資訊在信封、業務資料在
data。 違反的代價是路由器被每種 schema 綁死。 -
★ 用
json.RawMessage延遲解析,讓路由器/稽核/DLQ 處理器不必認識業務型別。 -
★★ 這件事必須第一天就做。事後加要改所有生產者與消費者,且過渡期要同時支援兩種格式。做進共用 library。
-
★ 標頭與信封兩邊都放,但要明定「不一致時以信封為準」,否則除錯時不知道信哪個。
-
★ 跨 broker 搬運時標頭容易掉(見 訊息橋接),信封欄位不會。
-
★ 用 protobuf + schema registry 或 CloudEvents 時,不要自己再發明一層信封。
-
信封不是放什麼都可以的地方。它會變成垃圾桶(塞進 tenant、locale、feature flag…)——每個欄位都要有明確的擁有者與用途。
-
信封欄位一旦有消費者依賴,就是公共契約,不能隨便改名。
🔗 相關工具
- 內容擴充器 Content Enricher(同為訊息轉換家族,改為補進資料)
- 正規化器 Normalizer(同為訊息轉換家族,改為統一格式)
- 企業整合模式
- 訊息 —— 三層結構總覽
- 格式指示器 —— 信封裡的版本欄位
- 關聯識別碼 —— 信封裡最重要的幾個 ID
- 寄物單 —— data 太大時
- 訊息路由器 —— 為什麼路由只看信封
- 訊息橋接 —— 標頭跨 broker 會掉
- 訊息匯流排 —— 把信封做進共用 library