rabbitMQ延时队列(TTL+DLX)

本文介绍RabbitMQ中的TTL(Time To Live)和死信队列(DLX)的概念及应用。详细解释如何设置消息和队列级别的过期时间,以及如何结合使用TTL与DLX实现延时队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TTL

  • TTL全称Time To Live (存活时间/过期时间)。
  • 当消息到达存活时间后,还没有被消费,会被自动清理。
  • RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间

例如下了订单为这个订单设置一个过期时间,当超过这个过期时间这个订单就失效了
在这里插入图片描述
设置过期时间方式一:声明队列的时候设置
为队列设置统一过期时间,时间范围内没有消费消息,消息将自动删除(消息从进入队列开始计算)
代码示例:

/**
     *  Expiration字段以微秒为单位表示TTL值,且与x-message-ttl具有相同的约束条件.因为Expiration字段必须是
     *         字符串类型,borker将只会接收以字符串形式表达的数字
     *         当同时指定了queue和message中的TTL值,两者中较小的值将会起作用
     * @return
     */
    @Bean
    public Queue TTLQueue() {
        Map<String, Object> map = new HashMap<>();
        map.put("x-message-ttl", 30000); // 队列中的消息未被消费则30秒后过期
        return new Queue("TTL_QUEUE", true, false, false, map);
    }

设置过期时间方式二:发送消息的时候设置消息过期时间
代码示例:

/**
     * ttl时间设置
     * @param message
     * @return
     */
    public Boolean setMessageTTl(String message) {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = message;
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
          /*
        Expiration字段以微秒为单位表示TTL值,且与x-message-ttl具有相同的约束条件.因为Expiration字段必须是
        字符串类型,borker将只会接收以字符串形式表达的数字
        当同时指定了queue和message中的TTL值,两者中较小的值将会起作用
         */
        MessagePostProcessor messageProperties = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //设置message的信息
                message.getMessageProperties().setExpiration("10000");
                //返回该消息
                return message;
            }
        };
        //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
        rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting2", map,messageProperties);
        return true;

    }

当项目中同一个队列同时设置了两种过期时间,那个会选择时间较短的那个过期时间执行。

如果一个消息队列中其他的消息正常但是中间的消息突然设置过期时间,则不会清理消息,但是在消息消费的时候会去判断这个消息的过期时间。
测试代码:

 /**
     * ttl时间设置
     * @param message
     * @return
     */
    public Boolean setMessageTTl(String message) {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = message;
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
          /*
        Expiration字段以微秒为单位表示TTL值,且与x-message-ttl具有相同的约束条件.因为Expiration字段必须是
        字符串类型,borker将只会接收以字符串形式表达的数字
        当同时指定了queue和message中的TTL值,两者中较小的值将会起作用
         */
        MessagePostProcessor messageProperties = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //设置message的信息
                message.getMessageProperties().setExpiration("5000");
                //返回该消息
                return message;
            }
        };
        for(int i=0;i<10;i++){
            if(i==5) {
                //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
                rabbitTemplate.convertAndSend("topicExchange", "queue_ttl", map, messageProperties);
            }else {
                //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
                rabbitTemplate.convertAndSend("topicExchange", "queue_ttl", map);
            }
        }

        return true;

    }

TTL小结

  • 设置队列过期时间使用参数:x-message-ttl,单位:ms(毫秒),会对整个队列消息统一过期。

  • 设置单独消息过期时间使用参数:expiration。单位:ms(毫秒),
    当该消息到达队列头部时(消费时),会单独判断这一消息是否过期(如果消息在中间生成,暂时没到达头部,就不会将消息从队列中移除。)

  • 如果两者都进行了设置,以时间短的为准。

死信队列

死信队列,DLX。Dead Letter Exchange(死信交换机) ,当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX 。

在这里插入图片描述
消息成为死信的三种情况:
1.队列消息长度到达限制;
2.消费者拒绝消息,basicNak/basicReject,并且不把消息重新放入原目标队列,requeue= false;
3.原队列存在消息过期设置,消息到达超时时间未被消费;

队列绑定死信交换机:
给队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key
在这里插入图片描述
死信队列:
1.声明正常的队列(test_queue_dlx) 和交换机(test_exchange_dlx)
2.声明死信队列(queue_dlx)和死信交换机(exchange_dlx)
3.正常队列绑定死信交换机
设置两个参数:
x-dead-letter-exchange:死信交换机名称
x-dead-letter-routing-key:发送给死信交换机的routingkey
spring boot中设置当时间过期时形成的死信队列
代码示例
1.生成交换机和队列

@Configuration
public class DlxRabbitConfig {





    /**
     * 正常队列
     * @return
     */
    @Bean
    public Queue dlxQueue(){
        Map<String, Object> map = new HashMap<>();
       // x-dead-letter-exchange:死信交换机名称
        map.put("x-dead-letter-exchange", "exchange_dlx");
        // x-dead-letter-routing-key:发送给死信交换机的routingkey
        map.put("x-dead-letter-routing-key","dlx_tt");
        //成为死信对设置队列的过期时间  ttl
        map.put("x-message-ttl",10000);
        //设置队列的长度
        map.put("x-max-length",10);
        return new Queue("queue_dlx",true,false,false,map);

    }


    /**
     * 死信队列
     * @return
     */
    @Bean
    public Queue dlxQueue1(){
        return new Queue("test_queue_dlx");
    }


    /**
     * 正常交换机
     * @return
     */
    @Bean
    public DirectExchange dlxExchange(){
        return  new DirectExchange("test_exchange_dlx");
    }

    /**
     * 死信交换机
     */
    @Bean
    public DirectExchange dlxExchange1(){
        return  new DirectExchange("exchange_dlx");
    }

    /**
     * 交换机和队列绑定
     * @return
     */
    @Bean
    public Binding bindingExchange(){
        return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("test_queue");

    }


    /**
     * 死信交换机和队列绑定
     * @return
     */
    @Bean
    public Binding bindingExchange1(){
        return BindingBuilder.bind(dlxQueue1()).to(dlxExchange1()).with("dlx_tt");
    }



}

2.测试代码

/**
     * 死信队列测试
     * @param message
     * @return
     */
    public Boolean deadExchange(String message) {
        String messageId = String.valueOf(UUID.randomUUID());
        String messageData = message;
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("messageData", messageData);
        map.put("createTime", createTime);
        rabbitTemplate.convertAndSend("test_exchange_dlx", "test_queue", map);
        return true;
    }

过程如下:当发送请求时会在队列(queue_dlx)存在一条消息,因为没有消费者,而这个队列设置了过期时间,当到达了过期时间,那么这个队列会将消息通过路由键发送到交换机(exchange_dlx),交换机将消息又通过路由键发送到队列(test_queue_dlx)中那么就可以看到这个消息了。
在这里插入图片描述
过一段时间后
在这里插入图片描述
接下来写一个监听类监听队列(test_queue_dlx)

@Component
public class DlxReceiver {


    final  private  String message="messageData";

    @RabbitHandler
    @RabbitListener(queues = "test_queue_dlx")
    public void process(Map testMessage) {
        System.out.println("test_queue_dlx队列 exchange_dlx消费者收到消息  : " + testMessage.toString());
        System.out.println("发送的消息:"+testMessage.get(message));
    }
}

测试结果:在这里插入图片描述
长度测试的时候队列的消息是先进先出的规则
代码示例

/**
     * 死信队列长度测试
     * @param message
     * @return
     */
    public Boolean deadExchangelenght(String message) {
        String messageId = String.valueOf(UUID.randomUUID());
        String dto = message;
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("messageId", messageId);
        map.put("createTime", createTime);
        for (int i=0;i<20;i++) {
            map.put("messageData", dto+i);
            rabbitTemplate.convertAndSend("test_exchange_dlx", "test_queue1", map);
        }
        return true;
    }

在这里插入图片描述

rabbitMQ延时队列

延时队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
需求:
1.下单后,30分钟未支付,取消订单,回滚库存。
2.新用户注册成功7天后,发送短信问候。

实现方式:
1.定时器(可以解决第一个问题,但是消耗性能)
2.延时队列
在这里插入图片描述
RabbitMQ延时队列实现方式:TTL+死信队列组合实现延迟队列的效果。
在这里插入图片描述
延时队列和之前演示死信队列差不多就不代码演示了!

在Spring Boot中使用RabbitMQ实现延迟队列,可以通过以下两种主要方法来完成: ### 1. 利用TTL(Time To Live)和死信队列(Dead Letter Exchange, DLX) 这种方法是通过设置消息的TTL属性和队列TTL属性,结合死信队列的机制来实现延迟队列的效果。具体步骤如下: - **定义交换机和队列**:首先需要定义一个普通交换机和一个死信交换机,以及一个普通队列和一个死信队列。普通队列需要配置其TTL值,并且设置死信交换机和死信路由键。 - **发送消息**:向普通交换机发送消息,并设置消息的TTL值。 - **处理消息**:当消息在普通队列中的生存时间超过设定的TTL值后,会被自动转发到死信交换机,进而被投递到死信队列中,由监听死信队列的消费者进行处理。 示例代码片段展示了如何监听延迟队列和死信队列: ```java private static final String DEAD_QUEUE_NAME = "test_dead_ttl_springboot_queue"; @RabbitListener(queues = "test_delay_ttl_springboot_queue") public void delay(Message message, Channel channel) throws IOException { String msg = new String(message.getBody(), "UTF-8"); System.out.println("msg : " + msg); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } @RabbitListener(queues = DEAD_QUEUE_NAME) public void delay2(Message message, Channel channel) throws IOException { String msg = new String(message.getBody(), "UTF-8"); System.out.println("msg : " + msg); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } ``` ### 2. 使用RabbitMQ的延迟插件 从RabbitMQ 3.5.7版本开始,可以使用`rabbitmq-delayed-message-exchange`插件来实现延迟队列。这种方法提供了更灵活的配置选项,并且可能提供更高的时间精度和可靠性。具体步骤如下: - **安装插件**:首先需要下载并安装`rabbitmq-delayed-message-exchange`插件。 - **声明自定义交换机**:在应用程序中声明一个带有`x-delayed-type`参数的自定义交换机。 - **发送消息**:发送消息时设置`x-delay`属性来指定延迟时间。 示例代码片段展示了如何声明一个带有延迟特性的交换机: ```java @Bean public CustomExchange delayedExchange() { return new CustomExchange("delayed_exchange", "x-delayed-message", true, false); } @Bean public Queue delayedQueue() { return QueueBuilder.durable("delayed_queue").build(); } @Bean public Binding bindingDelayedExchange(@Qualifier("delayedQueue") Queue queue, @Qualifier("delayedExchange") CustomExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("delayed_key").noargs(); } ``` 在发送消息时,可以通过设置`x-delay`属性来指定延迟时间: ```java MessageProperties props = new MessageProperties(); props.setDelay(5000); // 设置延迟时间为5秒 channel.basicPublish("delayed_exchange", "delayed_key", props, "Hello World".getBytes()); ``` 这两种方法各有优缺点,选择哪种方法取决于具体的业务需求和技术环境。如果需要更高精度的延迟控制和更复杂的延迟逻辑,推荐使用RabbitMQ的延迟插件;而对于简单的延迟需求,使用TTL和死信队列的方法更为简便。[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱上编程2705

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值