🌺The Begin🌺点点关注,收藏不迷路🌺

一、前言

在实际业务场景中,延迟任务无处不在:

  • 订单超时未支付,自动取消
  • 发货后未收货,自动确认
  • 预约通知、定时提醒
  • 验证码超时失效

这些场景都需要消息在指定延迟时间后才被消费,而 RabbitMQ 原生并不直接支持延迟队列,但我们可以通过死信队列 + TTL官方延迟插件 完美实现。

本文将详细讲解 RabbitMQ 延迟队列的 2 种实现方案,从原理、流程图、代码实战、优缺点、生产选型全方位覆盖,直接可用于生产环境。


二、基础认知:什么是延迟队列?

2.1 延迟队列定义

延迟队列 = 消息进入队列后,等待指定延迟时间,才会被消费者消费

  • 消息发送后,不会立即被消费
  • 等待 N 秒/分钟/小时后,自动触发消费
  • 本质:定时任务 + 消息队列

2.2 延迟队列核心应用场景

  1. 订单超时未支付自动关闭
  2. 未签收订单自动确认收货
  3. 定时通知、短信提醒
  4. 验证码超时失效
  5. 延迟重试机制

2.3 延迟队列实现流程图

生产者发送延迟消息

设置消息过期时间TTL

消息进入等待队列

等待TTL超时

消息变为死信,转发至死信队列

消费者监听死信队列,开始消费


三、RabbitMQ 实现延迟队列的 2 种方案

RabbitMQ 官方提供 2 种标准延迟队列方案

  1. 方案1:死信队列 + TTL(原生方案,无需插件)
  2. 方案2:延迟交换机插件(官方插件,推荐生产)

四、方案1:死信队列 + TTL 实现延迟(原生方案)

4.1 核心原理

  1. 创建普通队列(TT队列),不设置消费者
  2. 给队列/消息设置 TTL 过期时间
  3. 消息过期后,变成死信(Dead Letter)
  4. 死信自动转发到配置好的死信交换机(DLX)
  5. 消费者监听死信队列,实现延迟消费

4.2 核心组件

  1. 业务交换机(Biz Exchange)
  2. TT 等待队列(无消费者)
  3. 死信交换机(DLX Exchange)
  4. 死信队列(真正消费队列)

4.3 架构流程图

生产者

业务交换机

TT等待队列(设置TTL)

消息过期

转发至死信交换机DLX

死信队列

消费者监听消费

4.4 SpringBoot 代码实现

1. 配置类(交换机 + 队列 + 绑定)
@Configuration
public class TtlDelayConfig {

    // 1. 死信交换机
    public static final String DLX_EXCHANGE = "dlx.exchange";
    // 2. 死信队列
    public static final String DLX_QUEUE = "dlx.queue";
    // 3. TT等待队列(过期队列)
    public static final String TT_QUEUE = "tt.queue";
    // 4. 业务交换机
    public static final String BUSINESS_EXCHANGE = "business.exchange";

    // 死信交换机
    @Bean
    public DirectExchange dlxExchange() {
        return new DirectExchange(DLX_EXCHANGE);
    }

    // 死信队列
    @Bean
    public Queue dlxQueue() {
        return new Queue(DLX_QUEUE);
    }

    // TT等待队列(配置死信转发 + TTL)
    @Bean
    public Queue ttQueue() {
        Map<String, Object> args = new HashMap<>();
        // 死信交换机
        args.put("x-dead-letter-exchange", DLX_EXCHANGE);
        // 死信路由键
        args.put("x-dead-letter-routing-key", "dlx.routing.key");
        // 设置延迟时间:10秒(10000ms)
        args.put("x-message-ttl", 10000);
        return new Queue(TT_QUEUE, true, false, false, args);
    }

    // 业务交换机
    @Bean
    public DirectExchange businessExchange() {
        return new DirectExchange(BUSINESS_EXCHANGE);
    }

    // 绑定TT队列
    @Bean
    public Binding ttBinding() {
        return BindingBuilder.bind(ttQueue()).to(businessExchange()).with("tt.routing.key");
    }

    // 绑定死信队列
    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("dlx.routing.key");
    }
}
2. 生产者
@Component
public class DelayProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendDelayMsg(String msg) {
        // 发送到业务交换机 → TT队列
        rabbitTemplate.convertAndSend(
                TtlDelayConfig.BUSINESS_EXCHANGE,
                "tt.routing.key",
                msg
        );
        System.out.println("发送延迟消息,10秒后消费:" + msg);
    }
}
3. 消费者(监听死信队列)
@Component
public class DelayConsumer {
    // 监听死信队列
    @RabbitListener(queues = TtlDelayConfig.DLX_QUEUE)
    public void consume(String msg) {
        System.out.println("延迟消费成功,消息:" + msg + ",时间:" + new Date());
    }
}

4.5 优点

  • 无需插件,原生支持
  • 兼容性强,所有版本都能用
  • 实现简单

4.6 缺点

  • 只能固定延迟时间
  • 多个延迟等级需要创建多个队列
  • 存在队列头阻塞问题(前面消息未过期,后面消息无法过期)

五、方案2:延迟交换机插件实现(生产推荐)

5.1 核心原理

  1. 安装 RabbitMQ 官方 延迟消息插件rabbitmq_delayed_message_exchange
  2. 创建延迟交换机(x-delayed-message)
  3. 消息发送时携带延迟时间
  4. 插件内部延迟消息,到期后路由到队列
  5. 消费者直接监听业务队列

5.2 优势

  • 支持动态延迟时间(每条消息可不同)
  • 一个队列支持所有延迟级别
  • 无队列头阻塞问题
  • 生产环境标准方案

5.3 架构流程图

生产者发送消息,设置延迟时间

延迟交换机(插件)

插件内部暂存消息,计时等待

时间到达

路由到业务队列

消费者正常消费

5.4 插件安装步骤

  1. 下载对应版本插件:
    https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases
  2. 上传到插件目录:
    cp 插件.ez /usr/lib/rabbitmq/plugins/
    
  3. 启用插件:
    rabbitmq-plugins enable rabbitmq_delayed_message_exchange
    

5.5 SpringBoot 代码实现

1. 配置类
@Configuration
public class PluginDelayConfig {
    public static final String DELAY_EXCHANGE = "plugin.delay.exchange";
    public static final String DELAY_QUEUE = "plugin.delay.queue";
    public static final String ROUTING_KEY = "plugin.routing.key";

    // 延迟交换机(CustomExchange)
    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        // 交换机类型:direct/topic/fanout
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DELAY_EXCHANGE, "x-delayed-message", true, false, args);
    }

    // 业务队列
    @Bean
    public Queue delayQueue() {
        return new Queue(DELAY_QUEUE);
    }

    // 绑定
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(delayQueue()).to(delayExchange()).with(ROUTING_KEY).noargs();
    }
}
2. 生产者(动态设置延迟时间)
@Component
public class PluginDelayProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 发送延迟消息
     * @param msg 消息内容
     * @param delayTime 延迟时间(毫秒)
     */
    public void send(String msg, int delayTime) {
        rabbitTemplate.convertAndSend(
                PluginDelayConfig.DELAY_EXCHANGE,
                PluginDelayConfig.ROUTING_KEY,
                msg,
                message -> {
                    // 设置延迟时间
                    message.getMessageProperties().setDelay(delayTime);
                    return message;
                }
        );
        System.out.println("发送延迟消息:" + msg + ",延迟:" + delayTime / 1000 + "秒");
    }
}
3. 消费者
@Component
public class PluginDelayConsumer {
    @RabbitListener(queues = PluginDelayConfig.DELAY_QUEUE)
    public void consume(String msg) {
        System.out.println("插件延迟消费成功:" + msg + ",时间:" + new Date());
    }
}

5.6 优点

  • 动态延迟,每条消息可自定义时间
  • 一个队列支持所有延迟级别
  • 无队列头阻塞
  • 性能高、稳定可靠
  • 官方推荐生产使用

5.7 缺点

  • 需要安装插件
  • 不支持 Quorum 仲裁队列(3.9~3.12 需注意)

六、2 种延迟方案深度对比

对比维度 死信队列 + TTL 延迟交换机插件
实现方式 原生RabbitMQ 官方插件
延迟时间 固定,队列级别 动态,消息级别
队列数量 多延迟=多队列 一个队列通用
队列头阻塞 存在 不存在
灵活性 极高
生产推荐 一般 强烈推荐
复杂度

七、生产环境延迟队列最佳实践

  1. 优先使用插件方案,灵活、高效、无阻塞
  2. 订单、支付等核心场景必须开启持久化
  3. 消费者使用手动ACK,确保消息不丢失
  4. 结合死信队列做异常兜底
  5. 高可用集群使用镜像队列(插件暂不支持Quorum)

八、总结

  1. RabbitMQ 原生不支持延迟队列,必须通过方案实现
  2. 方案1(死信+TTL):无需插件、固定延迟、简单但有缺陷
  3. 方案2(延迟插件):动态延迟、灵活高效、生产首选
  4. 核心业务(订单超时)强烈推荐插件方案
  5. 简单固定延迟场景可使用死信方案

一句话结论

生产环境实现延迟队列,优先选择官方延迟插件方案,稳定、灵活、无坑!


在这里插入图片描述


🌺The End🌺点点关注,收藏不迷路🌺
Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐