代表性面试主题

后端面试:如何设计带安全重放的死信队列处理流程?

后端困难
OfferCC 编辑团队发布 更新

题干

请设计一个消息处理系统:失败消息进入死信队列,修复问题后可以安全重放。你如何保证不丢消息、不重复副作用,并避免重放再次拖垮系统?

题干与适用场景

设计一个异步消息流程:消息经过有限次重试后进入死信队列(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,保留原批次和新错误信息,暂停相关筛选条件并通知负责人。

追问五:如何估算重放速率?

根据消费者能力、下游配额、正常流量余量和恢复时间设定上限,先小批量压测,再逐步提高。

追问六:什么时候应该丢弃消息?

只有业务明确判定消息过期、撤销或无价值,并完成审计和责任确认后丢弃;保留摘要和原因。

公开来源

同类题目