訊息佇列 (Message Queue)
Queue 是一個緩衝層:Producer 把訊息丟進去就不管了,Consumer(worker)依自己的節奏拉出來處理。
它買到三件事 —— 非同步、解耦、削峰填谷;代價是最終一致、亂序風險、重複投遞,而且它不會增加你的系統容量,只是把過載往後推延。把 queue 當「解決過載的方法」是最常見的誤解 —— 真正的解法是 backpressure。
三個角色與基本流程
| 角色 | 職責 |
|---|---|
| Producer(生產者) | 把訊息送進 queue,送完即返回,不等處理結果 |
| Queue(佇列本體) | 存放訊息,通常支援持久化(落磁碟防丟) |
| Consumer / Worker | 從 queue 拉訊息處理,依自己的處理能力消化 |
┌───────── Queue ─────────┐
Producer ──put──► │ m5 m4 m3 m2 m1 │ ──poll──► Consumer (Worker)
(立即返回) └─────────────────────────┘ (自己的節奏處理)
訊息堆積在這裡 可水平擴展成多個 worker
訊息可以是任何形式:簡單字串、JSON、或序列化的複雜物件。
Queue 買到的三件事
1. 非同步(Asynchronous)
Producer 把訊息送進 queue 後不等 Consumer 處理完,立即返回。
使用者上傳圖片 → API 寫一筆訊息進 queue → 立刻回 200 OK("上傳成功")
└─ 背景 worker 稍後做壓縮/縮圖
好處:降低使用者等待時間、前端不被慢操作阻塞。這正是 10-Design-Patterns/03-Long-Running-Tasks 的核心模式。
2. 解耦(Decoupling)
Producer 不需要知道 Consumer 是誰、在哪、是否在線;Consumer 也不關心 Producer 的細節。
訂單系統 ──訂單訊息──► Queue ──► Email Service(寄確認信)
└─► Inventory Service(扣庫存)
好處:服務間依賴降到最低 → 各自獨立開發、部署、擴展。queue 後面的服務可以重啟或關閉,幾乎不影響整體系統(訊息會在 queue 等著)。
3. 彈性 / 削峰填谷(Elasticity / Load Leveling)
流量高峰時訊息堆在 queue,Consumer 可動態擴容消化;流量降低時縮減 worker 數量省資源。
請求暴增 1000/s ─┐ ┌─ Worker ×N(黑五時 scale out)
▼ │
[ Queue 接住峰值 ] ───►┤ 穩定以 200/s 速率消化
▲ │
平峰 50/s ───────┘ └─ Worker ×1(縮回去)
Queue 能平滑瞬時尖峰(burst),但若平均到達率 > 平均處理率,queue 只會無限增長、永遠處理不完。削峰填谷的前提是「長期吞吐量足夠,只是短期不均」。詳見下方 backpressure。
常見功能清單
| 功能 | 說明 |
|---|---|
| 持久化 Persistence | 訊息落磁碟,broker 掛了也不丟(代價是吞吐量略降) |
| 重試 Retry | 失敗訊息自動重投,可設最大次數與退避延遲 |
| 死信佇列 DLQ | 重試耗盡後丟進 DLQ,供除錯/稽核,不阻塞正常流量 |
| 延遲 / 排程訊息 | 控制訊息何時才被消費(如 15 分鐘後提醒) |
| 順序性 Ordering | 保證同一 partition 內訊息順序一致(非全域順序) |
| 多消費者模型 | 多個 consumer 平行處理;或 fan-out 給多個訂閱者 |
投遞語義(面試必考)
Queue 的可靠性核心是「訊息會被投遞幾次」。這幾乎一定會被深挖。
| 語義 | 保證 | 風險 | 代價 |
|---|---|---|---|
| At-most-once | 最多一次(可能丟) | 訊息遺失 | 最快、最簡單 |
| At-least-once | 至少一次(可能重複) | 重複處理 | 多數 queue 預設(SQS 等) |
| Exactly-once | 恰好一次 | 幾乎做不到純正 | 昂貴;多靠去重模擬 |
純正的 exactly-once 在分散式環境幾乎不可能(網路會丟 ack,broker 不知道 consumer 到底處理完沒)。主流做法是 at-least-once 投遞 + 讓 consumer 冪等(idempotent):用唯一的 message_id 去重,重複收到同一條就跳過。這個組合等效於業務上的 exactly-once。詳見 12-Ops-and-Reliability/03-Reliable-Delivery 與 01-Networking/07-Fault-Handling。
訊息確認流程(為什麼會重複):
Consumer 收訊息 → 處理 → 送 ack 給 broker → broker 刪除訊息
▲
若 consumer 處理完但 ack 還沒送到就掛了
→ broker 沒收到 ack → 超時後重投 → 同一訊息被處理兩次
順序性與分區擴展
順序保證
- 多數 queue 是 FIFO(先進先出),訊息按接收順序處理。
- 但一旦有多個 consumer 平行拉取,全域順序就無法保證(worker A 比 worker B 快)。
- 真正的順序保證只在單一 partition 內成立。需要嚴格順序 → 把相關訊息用同一個 partition key 路由到同一分區(如同一
user_id的事件進同一 partition)。
用分區擴展(Scaling with Partitions)
┌─ Partition 0 (key hash %3 == 0) ─► Worker group A
Producer ─路由─►├─ Partition 1 (key hash %3 == 1) ─► Worker group B
(partition key)└─ Partition 2 (key hash %3 == 2) ─► Worker group C
跟 05-Database-Advanced/02-Sharding 概念一致:選對 partition key,讓相關訊息進同一分區(保順序),又讓整體分散到多台(保吞吐)。這正是 09-Messaging-Coord/03-Kafka 的核心設計。
Backpressure(Queue 最大的陷阱)
若系統每秒只能處理 200 個請求,卻持續收到 300/s,queue 永遠清不完、無限堆積、延遲無限增長。Queue 沒有增加任何處理能力,只是把問題藏起來、往後推。
Backpressure = 在 queue 過載時,反向減緩 producer 的生產速度:
Queue 將滿 ──► 向 Producer 回壓:
① 拒絕新訊息(回 429 / 503 給上游或使用者)
② 降低接受速率(rate limit)
③ 讓 producer 阻塞 / 等待(有界 queue 寫滿時 block)
這與 12-Ops-and-Reliability/02-Overload-Protection 的 load shedding 是同一套思路:寧可早一點拒絕一部分請求,也不要讓整個系統雪崩。也呼應 10-Design-Patterns/02-Scaling-Writes 處理寫入洪峰的策略。
同步場景慎用 Queue
如果有 < 500ms 的延遲要求,加入 queue 後訊息要排隊、等 worker 拉取、可能還要重試 —— 幾乎可確定會打破這個限制。Queue 天生是非同步工具。
判斷準則:呼叫方需要立即拿到結果(同步 RPC、API 即時回應)→ 不要用 queue;呼叫方只要「之後會做完」→ queue 是好選擇。
常見產品與服務
| 類別 | 產品 | 定位 / 特點 |
|---|---|---|
| 雲端託管 | AWS SQS | 全託管、at-least-once、標準 / FIFO 兩種 |
| Google Pub/Sub | 全託管 pub/sub,自動擴展 | |
| Azure Queue Storage | 簡單可靠的雲端 queue | |
| 開源 broker | RabbitMQ | AMQP 協定,路由靈活(exchange/binding),低延遲 |
| Kafka | 高吞吐 log stream,分區 + 可重播,偏資料管線 | |
| ActiveMQ | 老牌 JMS/AMQP broker | |
| Redis Stream | 輕量、低延遲,附在 Redis 上 |
RabbitMQ(傳統 message broker):訊息被消費後即刪除;強在靈活路由(topic/fanout/direct exchange)、每訊息確認、任務分派(work queue)。適合「命令式」任務分發、複雜路由、較低吞吐。
Kafka(分散式 log):訊息保留一段時間且可重播,consumer 自己記 offset;強在超高吞吐、多訂閱者各自消費同一份資料、事件溯源。適合資料管線、串流、需要重播歷史。
一句話:要靈活路由與任務隊列選 RabbitMQ;要高吞吐事件流與可重播選 Kafka。細節見 09-Messaging-Coord/03-Kafka。
適用場景
| 場景 | 說明 |
|---|---|
| 任務非同步化 | 雲端照片服務:上傳後把昂貴的影像處理丟 queue,多個 worker 拉取平攤 |
| 流量削峰 | Uber 叫車尖峰:queue 緩衝暴增請求,系統以可控速率消化,不打掛伺服器 |
| 服務解耦 | 訂單系統與寄信系統透過 queue 連接,互不影響、各自擴展 |
| 事件驅動架構 | queue / topic 當 event bus,一個事件 fan-out 觸發多個下游服務 |
面試話術
最有說服力的回答是先講清楚為什麼需要:「這裡使用者上傳圖片後不需要等壓縮完成,所以我用 queue 讓 API 立即回應,背景 worker 非同步處理 —— 這同時帶來解耦(壓縮服務可獨立擴展)和削峰(黑五尖峰被 queue 接住)。」當你需要非同步 / 解耦 / 削峰時才引入 queue,而不是預設加上。
被追問時主動講出三個深度點,立刻顯示你懂:
- 「這條訊息可能被處理兩次」 → 我會用 at-least-once + consumer 冪等(
message_id去重)。 - 「Queue 不會增加容量」 → 若平均到達率超過處理率,我需要 backpressure(回壓 / 拒絕)而不是無腦堆 queue。
- 「失敗訊息怎麼辦」 → 重試 N 次後進 DLQ,對 DLQ 深度設告警,人工或自動回放。
常見 Deep Dive
同步呼叫:caller 等 callee 回應,耦合緊、延遲低、失敗立即知道。Queue:caller 丟完就走,最終一致、解耦、能削峰,但延遲高且失敗是非同步發現。判準:需要即時結果或強延遲 SLA → 同步;只要「之後會做完」且想解耦/削峰 → queue。
訊息重試耗盡(如試 5 次仍失敗)後,不丟棄也不無限重試,而是移到一個獨立的 DLQ。好處:①不阻塞正常流量(一條毒訊息不會卡住整條 queue)②可檢視失敗訊息做除錯與稽核 ③修好 bug 後可把訊息回放。實務上一定要對 DLQ 深度設 CloudWatch / 監控告警,否則訊息默默死在裡面沒人知道。連結 12-Ops-and-Reliability/04-Observability。
At-least-once 必然會重複。做法:①給每條訊息唯一 message_id ②consumer 處理前先查「這個 id 處理過沒」(存在 Redis / DB 的去重表)③處理過就跳過。或讓操作本身冪等(如 SET balance=100 而非 balance += 10,重複執行結果相同)。這是 12-Ops-and-Reliability/03-Reliable-Delivery 的核心技巧。
多個 worker 平行拉取,誰先處理完無法控制(worker A 卡住、worker B 先跑完後面的訊息)。救法:把需要保序的訊息用同一 partition key 路由到同一分區,且該分區只給一個 consumer。代價是該 key 的吞吐量被單 consumer 限制。這是順序與吞吐的根本取捨,Kafka 的 partition 模型就是這樣設計。
先分辨原因:①短期尖峰 → scale out consumer 消化即可 ②長期到達率 > 處理率 → 加 worker 也只是延後爆炸,必須 backpressure(回壓 producer、拒絕/限流新訊息、回 429)。永遠監控 queue 深度 與 消費延遲(consumer lag) 作為告警指標。
它解決的是「時間上的不均」與「服務間耦合」,不解決「整體吞吐量不足」。容量不夠就是要加機器或限流,queue 只會把過載藏得更深。
自我測驗重點
| 問題 | 重點 |
|---|---|
| Queue 買到哪三件事 | 非同步、解耦、削峰填谷(彈性) |
| 投遞語義主流是哪個 | at-least-once + consumer 冪等 ≈ 業務 exactly-once |
| 為什麼會重複投遞 | ack 在處理後丟失 → broker 超時重投 |
| Backpressure 解決什麼 | queue 掩蓋容量不足;過載時回壓/拒絕 producer |
| DLQ 的用途 | 重試耗盡的訊息隔離,供除錯/稽核/回放,不阻塞主流程 |
| 多 consumer 為何亂序 | 平行處理無法控制完成順序;靠 partition key + 單 consumer |
| 何時不該用 queue | 同步、強延遲 SLA(< 500ms)的場景 |
| RabbitMQ vs Kafka | 靈活路由/任務隊列 vs 高吞吐可重播 log stream |
Related Notes
- 09-Messaging-Coord/03-Kafka — 高吞吐、可重播的 log-based queue,partition 模型是 queue 擴展的具體實作
- 09-Messaging-Coord/01-Data-Pipeline — queue 是資料管線中連接各階段的標準緩衝層
- 10-Design-Patterns/03-Long-Running-Tasks — 「API 立即回應、背景 worker 非同步處理」就是 queue 的經典模式
- 10-Design-Patterns/02-Scaling-Writes — 用 queue 緩衝寫入洪峰、削峰填谷
- 12-Ops-and-Reliability/03-Reliable-Delivery — at-least-once + 冪等 + DLQ 是可靠投遞的核心
- 12-Ops-and-Reliability/02-Overload-Protection — backpressure 與 load shedding 是同一套過載保護思路
- 01-Networking/07-Fault-Handling — 重試、超時、冪等都是故障處理的基本功
- 05-Database-Advanced/02-Sharding — partition key 路由與分片同源,相關資料進同一分區
- 11-Infrastructure/04-Serverless — SQS 觸發 Lambda 是 queue 驅動 serverless 的常見組合
- 12-Ops-and-Reliability/04-Observability — queue 深度與 consumer lag 是必監控的告警指標