死信与延迟
消费失败的兜底机制(死信)和定时触发机制(延迟),是 MQ 生产实践的两个必备组件。
消费重试
重试耗尽进死信
| 重试方式 | 机制 | 适用 |
|---|---|---|
| 消费者内重试 | 捕获异常重试 N 次 | 简单场景(限流/瞬时故障) |
| 延迟重投 | 失败消息延迟后重新消费 | 下游恢复需要时间 |
| 死信队列 | 重试耗尽转人工处理 | 系统性错误(数据问题) |
重试要有上限:无限重试 = 队列阻塞 + 下游持续被打。
死信队列(DLQ)
- 死信:重试耗尽仍失败的消息,转入专用队列
- 价值:隔离问题消息,不阻塞主队列;可监控积压、人工修复、重放
- 设计要点:
| 要点 | 说明 |
|---|---|
| 死信带原因 | 记录失败原因(错误信息、重试次数) |
| 监控告警 | DLQ 积压 = 系统性 bug 的信号 |
| 重放机制 | 修复后能重新投递(工具 + 人工审核) |
| 分区隔离 | 不同业务不同 DLQ |
延迟队列
延迟消息 = 到了指定时间才可见(下单 30 分钟未支付自动取消)。
| 实现 | 机制 | 精度 | 复杂度 |
|---|---|---|---|
| RabbitMQ 延迟插件 | x-delayed-message 交换机 | 秒级 | 低 |
| 定时任务扫表 | 数据库状态 + 轮询 | 分钟级 | 低(量小) |
| Redis zset | 到期时间作分数,轮询取到期任务 | 秒级 | 中 |
| Kafka 分层时间轮 | 时间轮 + 延迟索引 | 秒级 | 高(Kafka 2.x+) |
Redis zset 方案(高频考点):
ZADD delay_queue <到期时间戳> <任务>
轮询: ZRANGEBYSCORE delay_queue 0 <now> → ZREM 取出 → 处理
(取出要 ZREM 删除, 否则下次轮询重复取出)面试追问
- 重试什么时候放弃? 重试 N 次(指数退避)仍失败进死信。无限重试是灾难
- 死信队列的价值? 隔离问题消息不阻塞主队列 + 监控积压定位系统问题 + 人工修复重放
- 延迟队列怎么做? RabbitMQ 延迟插件、Redis zset(时间戳作分数)、时间轮。按精度和量级选
- 死信和延迟的区别? 死信是失败兜底(转入人工),延迟是定时触发(到时可见)
- DLQ 积压说明什么? 系统性 bug(数据格式、逻辑错误),要告警和人工介入,不是调大重试能解决的