P94 RabbitMQ 死信队列、延时队列
面试题:RabbitMQ 的死信队列和延时队列是什么?怎么实现?
1. 死信队列(DLX, Dead Letter Exchange)
消息变成"死信"的三种情况:
- 被消费者拒绝(basicNack/basicReject 且 requeue=false);
- 消息 TTL 过期;
- 队列达到最大长度(溢出)。
死信去向:配置死信交换机(DLX)+ 死信路由键,消息被投递到死信队列:
java
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx.exchange");
args.put("x-dead-letter-routing-key", "dlx.routing");
channel.queueDeclare("business_queue", true, false, false, args);用途:
- 失败消息兜底:消费失败进死信队列,单独监控/人工处理;
- 延迟消息:配合 TTL 实现(见下)。
2. 延时队列实现(三种方式)
① 死信队列 + TTL(经典方案)
text
生产者 → 普通队列(设置消息 TTL 30 分钟,无消费者)
消息过期 → 死信 → 投递到真实业务队列 → 消费者处理- 用
x-message-ttl(队列级)或消息的expiration属性; - 注意:队列级 TTL 是按头部消息计时的,队头不消费会阻塞后续消息过期(插队问题);
- 缺点:延迟时间不精确、积压时可能提前过期(队头阻塞)。
② 延迟插件(rabbitmq_delayed_message_exchange,推荐)
text
安装延迟插件 → 声明 x-delayed-message 类型交换机
发布时设置 x-delay 毫秒 → 到期后路由到队列java
props.setHeader("x-delay", 30 * 60 * 1000);- 延迟精确、无队头阻塞问题;
- 基于内存/磁盘存储,需注意插件版本兼容。
③ 定时任务扫表(不依赖 MQ)
订单场景常用"MQ 延迟 + DB 扫表兜底"双保险(见 P06)。
3. 加分点
- 说清"死信队列本质是队列的异常出口";
- 追问"TTL 和延迟消息的区别":TTL 是过期,延迟是到期投递,死信队列+TTL 是借过期实现延迟;
- 提到消费端 nack 时别无限 requeue,否则死循环,应进死信队列;
- 生产推荐:延迟插件(精确)或 RocketMQ 定时消息。
一句话总结
死信队列是消息被拒绝/过期/溢出后的兜底出口(DLX 投递);延时队列两种主流实现:TTL + 死信队列(有队头阻塞缺陷)和 x-delayed-message 延迟插件(精确推荐),订单超时等场景再配合定时扫表兜底。