Lecture06-RabbitMQ-延迟队列(TTL+死信)

文章目录

    [toc]

    单词词性音标含义
    ttln./tiː tiː el/生存时间(消息过期时间)
    dead lettern./ded ˈletə(r)/死信(无法被消费的消息)
    dlxn./diː el eks/死信交换机(死信转入的交换机)
    expirev./ɪkˈspaɪə(r)/过期(消息超时未被消费)
    delayv./n./dɪˈleɪ/延迟(推迟一段时间)
    retryv./n./ˈriːtraɪ/重试(失败后再次尝试)

    Lecture06-RabbitMQ-延迟队列(TTL+死信)


    1、需求场景

    电商系统:用户下单后,30 分钟检查该订单是否已支付,未支付则自动取消(或发送提醒)。

    如果不用延迟队列,常规做法是"定时任务轮询订单表",又笨又费资源,关键是不准确:你检测到它过期时,已经过期很久了。

    延迟队列的思路是:把"检查订单支付"这个动作本身延迟 30 分钟再执行——消息到点(30 分钟后)才被真正的业务消费者收到。


    2、原理:延迟消息 + 死信队列

    核心是两条队列 + 一个死信交换机:

    1. 设一个延迟队列:给它设置消息 TTL(生存时间)= 30 分钟,并绑定死信交换机 DLX
    2. 生产者把"检查订单"这条消息发到延迟队列
    3. 延迟队列 没有消费者去消费它(它就是用来"存"消息、让消息过期的)
    4. 30 分钟后消息到期 → 被自动转投到死信交换机 DLX
    5. 死信交换机按路由键把消息路由到业务队列
    6. 业务消费者监听业务队列 → 到点(30 分钟后)才收到消息,执行"检查订单是否已支付"
    [生产者] --发消息--> [延迟队列 TTL=30min] --(30min后到期)--> [死信交换机 DLX] --路由--> [业务队列] --投递--> [业务消费者: 检查订单支付]
    

    一句话:延迟队列不消费,只让消息"躺到过期",过期后借死信交换机转给真正的业务队列。业务消费者 30 分钟后才拿到消息。


    3、创建所需设备

    ①所需设备清单

    • 正常交换机:atguigu.delay.normal.exchange
    • 正常交换机绑定正常队列的路由键:atguigu.delay.normal.routing.key
    • 正常队列:atguigu.delay.normal.queue
    • 死信交换机:atguigu.delay.dead.exchange
    • 死信交换机绑定死信队列的路由键:atguigu.delay.dead.routing.key
    • 死信队列:atguigu.delay.dead.queue

    ②特殊设定

    正常队列需要在消息超时后把消息转到死信队列,所以需要增加额外设置:

    image-20261018212509152

    image-20261018212544468

    image-20261018212617172

    image-20261018212649237


    4、Java 代码

    ①生产者端

    把消息发送给正常队列:

    @Test
    public void test02() {
        String exchangeName = "atguigu.delay.normal.exchange";
        String routingKey = "atguigu.delay.normal.routing.key";
        String message = "This message will be dead.";
        rabbitTemplate.convertAndSend(exchangeName, routingKey, message);
    }
    

    ②消费者端

    监听死信队列:

    @RabbitListener(queues = {"atguigu.delay.dead.queue"})
    public void receiveDeadMessage(String deadMessage, Message message, Channel channel) throws IOException {
    
        log.info("死信消息内容:" + deadMessage);
    
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
    

    5、验证

    • 教学时把 TTL 改短(如 10 秒),发一条消息:
      1. 立即看 延迟队列:有 1 条 ready 消息(没有消费者消费它)
      2. 此时业务队列是空的、消费者没收到
      3. 10 秒后:延迟队列里消息消失,业务队列出现该消息,消费者收到 → 说明延迟生效 ✅
    • 也可以在 Management UI 里直接看两个队列的 ready 数变化。

    6、要点 / 常见问题

    • 为什么延迟队列没有消费者? 因为它只负责"存"消息让消息到期;真正的业务在死信后的业务队列上消费。
    • TTL 设在哪? 设的是队列级 TTL(x-message-ttl),即该队列里每条消息最多存活 30 分钟,到期转死信。
    • 这个方案在 RabbitMQ 4 还成立吗? 成立。TTL + 死信机制在 RabbitMQ 4 依然有效(经典队列和 Quorum 队列都支持)。
    • 用插件行不行? rabbitmq_delayed_message_exchange 插件在 3 里可以正常使用;但是 4 里官方已标记"不再维护",不推荐新项目依赖。

    7、本节验证

    • 理解延迟队列(TTL+死信)的原理(延迟队列不消费,到期转死信交换机)✅
    • 会用 Spring Boot 声明延迟队列/死信交换机/业务队列三件套 ✅
    • 会验证延迟生效(把 TTL 改短观察)✅
    • 分清"延迟队列(定时调度)"与"Quorum 延迟重试(失败退避)"✅
    end