題幹與適用場景
設計一個异步訊息流程:訊息经過有限次重試後进入死信佇列(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,保留原批次和新錯誤信息,暂停相关筛选条件並通知负责人。
追问五:如何估算重播速率?
根据消费者能力、下游配额、正常流量余量和恢復時间设定上限,先小批量压测,再逐步提高。
追问六:什么時候應该丢弃訊息?
只有业務明确判定訊息過期、撤销或无价值,並完成审计和责任确認後丢弃;保留摘要和原因。