題幹與適用場景
設計一個非同步訊息流程:訊息經過有限次重試後進入死信佇列(DLQ),營運人員修復根因後可以按條件重播。說明訊息如何進入 DLQ、如何調查、如何選擇批次、如何避免重複副作用,以及如何保護正常流量。
這道題適用於後端、平台、基礎設施和資料工程職位。Amazon 的軟體開發面試主題強調運用技術解決問題,而不是背誦細節。AWS 文件說明 DLQ 用於隔離未成功消費的訊息,並應配合告警和重試次數;Google Pub/Sub 文件提供死信主題與 replay/seek 語義。重點是故障邊界和營運閉環。
面試官考察點
- 區分暫時性錯誤、毒丸訊息、業務拒絕和過期訊息。
- DLQ 是否保存版本、租戶、分區、追蹤 ID 和失敗原因。
- 重播是否具備冪等鍵、範圍、速率限制、審批和停止條件。
- 是否明確順序、保留期、重複投遞和至少一次語義。
- 是否用指標驗證恢復,而不是只說“重新放回主佇列”。
回答前需要釐清的問題
- 投遞語義是至少一次、至多一次,還是業務上要求有效一次?
- 哪些失敗可以重試,哪些必須人工處理或丟棄?
- 訊息保留多久?過期訊息還有價值嗎?
- 同一聚合鍵是否必須有序?
- 消費者副作用是否支援冪等或去重?
- 誰可以查看、重播和刪除訊息,是否需要審計?
- 正常流量 SLO、佇列容量和重播容量是多少?
- 重播失敗後進入同一 DLQ 還是獨立 replay-DLQ?
30 秒回答框架
“我先定義至少一次投遞、保留期和順序鍵。消費者對可重試錯誤使用有上限的退避,對毒丸或業務拒絕記錄原因後轉入 DLQ。DLQ 保存原始訊息、嘗試次數、消費者版本和追蹤 ID。修復後按租戶、版本或時間窗建立重播批次,先隔離驗證,再限速投遞;消費者用冪等鍵保護副作用。監控 DLQ 深度、最老訊息、重播失敗率和重複率,超過閾值就暫停。”
分步驟深入解答
步驟一:定義失敗狀態機
正常佇列、重試、DLQ、人工修復和 replay-DLQ 應是可區分狀態。不可重試的業務拒絕不能無限重試。
步驟二:設計訊息元資料
保存事件 ID、業務冪等鍵、產生時間、租戶或分區、模式版本、嘗試次數、原始追蹤 ID 和錯誤分類。原始載荷不可變,重播只改變投遞元資料。
步驟三:確定重試與入 DLQ 規則
設置最大接收次數、退避上限和保留期。AWS SQS 文件指出 DLQ 有源佇列和區域約束,並應配置告警。過期或撤銷訊息應記錄原因後人工處置。
步驟四:設計安全重播流程
重播請求包含篩選條件、目標版本、速率上限、最大批量、審批人和過期時間。先抽樣驗證,再分批投遞;重播佇列與正常佇列隔離。
| 控制點 | 目的 | 失敗動作 |
|---|---|---|
| 冪等鍵 | 防止重複副作用 | 拒絕或返回已處理結果 |
| 速率上限 | 保護消費者和下游 | 暫停重播 |
| 批次範圍 | 限制影響面 | 縮小篩選條件 |
| 審批與審計 | 追蹤責任 | 阻止未授權操作 |
| replay-DLQ | 隔離再次失敗 | 生成新診斷批次 |
步驟五:處理順序與並發
需要有序時按聚合鍵分組,避免同鍵的正常消費與重播並行。不要聲稱佇列天然提供端到端 exactly-once。
步驟六:保護副作用
消費者以事件 ID 或業務冪等鍵做條件寫。支付、郵件等不可回滾副作用需要冪等請求鍵和結果查詢;重播工具不能假設刪除訊息就足夠。
步驟七:可觀測和營運閉環
監控 DLQ 深度、最老訊息年齡、錯誤分類比例、重播吞吐、失敗率、重複副作用和下游延遲。每個批次記錄操作者、原因、時間、結果和停止記錄。
步驟八:容量、兼容和停止
估算最大重播速率,保證新消費者兼容舊模式。根因未修復、下游過載或重複率上升時立即暫停,保留原始證據。
高品質示範回答
“我設計的是至少一次投遞的訂單事件流程。網路超時最多重試 5 次並退避;模式錯誤、權限拒絕和過期事件進入 DLQ。每條訊息保存事件 ID、訂單 ID、租戶、模式版本、首次入隊時間、嘗試次數、錯誤分類和 trace ID,原始載荷不可變。
修復消費者後,營運人員建立批次,指定租戶、時間範圍、目標版本、每秒上限、審批人和過期時間。先抽取 50 條在隔離消費者上驗證,再以正常流量 10% 的速率投遞。訂單狀態表以事件 ID 做冪等條件寫,支付呼叫使用同一業務冪等鍵。相同訂單的正常訊息和重播訊息不能並行。
告警覆蓋最老訊息、重播失敗率、重複寫入和下游延遲;任何閾值觸發都暫停批次並把再次失敗的訊息放入 replay-DLQ。批次記錄篩選、版本、操作者、結果和停止原因。”
常見錯誤
- 把 DLQ 當垃圾桶,不保留錯誤分類和原始證據。
- 無限重試毒丸訊息,持續阻塞正常流量。
- 只說“放回主佇列”,沒有批次、審批和速率控制。
- 假設佇列提供端到端 exactly-once。
- 沒有冪等鍵,重播造成重複扣款、發信或狀態推進。
- 忽略順序鍵,導致舊事件覆蓋新狀態。
- 只監控佇列長度,不監控最老訊息和業務結果。
- 重播與正常消費共用容量,恢復流量再次過載。
追問及應對
追問一:為什麼不直接增加最大重試次數?
重試適合暫時性故障;毒丸、模式錯誤和永久業務拒絕會浪費容量。次數應結合故障類型、保留期和等待成本決定。
追問二:重播時怎樣保證同一訂單有序?
按訂單鍵分區,阻止同鍵並行,並在正常消費和重播之間建立互斥;若允許無序,要說明狀態轉換如何防止舊事件覆蓋新狀態。
追問三:下游沒有冪等介面怎麼辦?
建立本地去重和結果查詢;對不可逆副作用改為人工核對、補償流程或供應商支援的冪等機制。
追問四:重播再次失敗怎麼辦?
進入獨立 replay-DLQ,保留原批次和新錯誤資訊,暫停相關篩選條件並通知負責人。
追問五:如何估算重播速率?
根據消費者能力、下游配額、正常流量餘量和恢復時間設定上限,先小批量壓測,再逐步提高。
追問六:什麼時候應該丟棄訊息?
只有業務明確判定訊息過期、撤銷或無價值,並完成審計和責任確認後丟棄;保留摘要和原因。