🎯 什麼情境該想到我

當你「要透過訊息通道傳送一份資料」的時候:把資料封裝成一個可被訊息系統傳輸的單位。

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

意圖:把要傳輸的資料包成訊息——訊息系統能傳送的最小資料單位。

做法 / 結構要點:

  • 一則訊息通常分兩部分:標頭(header) 給訊息系統用(如識別碼、回覆位址、型別、過期時間),內容(body) 是實際承載的資料。
  • 訊息可扮演不同角色:命令(要對方做事)、文件(傳遞資料)、事件(通知發生了什麼)。
  • 發送方把資料寫入訊息,訊息系統負責在通道上傳遞它。

★ 一則訊息的三層結構(每一層的責任不同)

┌─ 通道/broker 的 metadata ── offset、partition、timestamp、delivery count(★ 你不擁有)
├─ 標頭 headers ───────────── ★ 路由與運維用:message-id、correlation-id、
│                              content-type、trace-parent、schema-version
└─ 內容 body ──────────────── ★ 業務資料。路由不應該需要解開它

★★ 最重要的一條原則:路由與運維資訊放標頭,業務資料放內容。
路由器需要解開 body 才能決定去向 → 路由器就得認識每一種 schema,
schema 一改就要改路由器(見 訊息路由器、信封包裝器)。

★ Go 的訊息型別設計

// ★ 用信封包裝,讓 body 保持不透明
type Envelope struct {
    MessageID     string          `json:"message_id"`      // ★ 冪等去重用([[冪等接收者]])
    CorrelationID string          `json:"correlation_id"`  // ★ 串起一次業務流程([[關聯識別碼]])
    Type          string          `json:"type"`            // ★ "orders.order.placed"
    SchemaVersion int             `json:"schema_version"`  // ★ 讓消費者能判斷自己認不認得
    OccurredAt    time.Time       `json:"occurred_at"`     // ★ 事件發生時間 ≠ 訊息送出時間
    Data          json.RawMessage `json:"data"`            // ★ 延遲解析:路由不必認識內容
}

★ OccurredAt 與「broker 的 timestamp」要分清楚:
重送、重放、積壓都會讓兩者差很遠。業務邏輯一律用 OccurredAt。

★★ 訊息大小:這是最容易踩的硬限制

實測/查證 2026-09-25:

broker上限
SQS★ 1,048,576 bytes(1 MiB)——注意:不再是舊資料常說的 256 KB。要更大要用 Extended Client(把 body 放 S3)
Kafka由 message.max.bytes/max.message.bytes 決定,預設約 1 MB 量級;★ 改大會連帶影響 broker 記憶體與複製延遲
RabbitMQ可設定,但大訊息會嚴重拖慢佇列

★ 超過限制的正確做法不是調高上限,是 寄物單:
把大 payload 放物件儲存,訊息裡只放 key。

// ❌ 把 5 MB 的 PDF 塞進訊息
// ✅ 寄物單
type DocReady struct {
    DocID  string `json:"doc_id"`
    S3Key  string `json:"s3_key"`      // ★ 訊息只帶位址
    SHA256 string `json:"sha256"`      // ★ 讓消費者能驗證拿到的是同一份
}

★ 訊息的三種語義(混用是事件驅動架構的頭號設計錯誤)

命令訊息事件訊息文件訊息
意思「請做這件事」「這件事已發生」「這是資料」
接收者數★ 恰好一個★ 零到多個任意
命名祈使句 CreateOrder★ 過去式 OrderPlaced名詞
可以拒絕✅❌ 已經發生了—

★ schema 演進:訊息的 schema 是跨行程契約

★ 一定要能容忍「消費者是舊版」:
  ① 只新增「可選」欄位,不改既有欄位的意義
  ② 不重用 protobuf 的欄號、不改型別
  ③ 不相容的變更 → 開一條新版本通道(v2),兩條並行直到舊消費者下線

→ 完整規則見 工具-資料編碼與演進。

🧪 我實際套用的紀錄

  • (待填)

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

  • 訊息通常有大小限制,超大資料需改用參照(如只傳位址/ID)而非整包塞進去。

  • 標頭與內容的格式需雙方(或透過轉換器)達成共識,否則接收端無法解讀。

  • ★★ 路由資訊放標頭、業務資料放內容。路由器要解開 body 才能決定去向,就會被 schema 綁死。

  • ★★ SQS 的上限是 1 MiB(2026-09-25 查證),不是舊資料常說的 256 KB。 版本敏感的數字要查證。

  • ★ 超過大小限制要用 寄物單,不要調高 broker 上限——調大會影響記憶體與複製延遲。

  • ★ 訊息一定要有 message-id(冪等)與 correlation-id(追因果)。 事後補很痛。

  • ★ 事件發生時間 ≠ 訊息送出時間 ≠ broker timestamp。業務邏輯用前者。

  • ★ 命令/事件/文件的語義不可混用。把命令發成事件 → 兩個服務都處理 → 寄兩封信。

  • schema 是跨行程契約。消費者可能是舊版,只能做向後相容的變更。

  • 不要在訊息裡放敏感資料。broker 有 retention、有備份、有日誌——那些地方都會留下來。

🔗 相關工具