RabbitMQ 高级特性——TTL

文章目录

  • 前言
  • TTL
    • [设置消息的 TTL](#设置消息的 TTL)
    • [设置队列的 TTL](#设置队列的 TTL)

前言

对于前面讲到的重试机制中,当确认策略为 MANUAL 手动确认的时候,如果消费者出现了程序逻辑错误,那么消息就无法被争取处理,那么就会执行 basicNack 方法,如果我们的 basicNack 方法的第三个参数的值为 true 的话,在只有这一个消费者的情况下,这个消息就会反复重新进入队列并且返回投递给这个消费者,那么在这个队列中的后面的消息就无法被提递给消费者,这样就导致了消息积压,那么如何处理这个问题呢?答案就是为消息或者队列设置 TTL(ime-To-Live)生存时间,当消息或者队列存在一段时间后就会被丢弃或者投递到死信队列中。那么这篇文章将介绍 RabbitMQ 中的 TTL。

TTL

TTL(Time-to-Live)过期时间,RabibtMQ 可以对队列和消息设置 TTL。当消息到达存活时间之后,如果该消息还没有被消费,那么就会被自动处理掉。

就是我们平时购物的时候,如果下单超过 24 消失还没有付款的话,订单就会被自动取消。

设置消息的 TTL

RabbitMQ 有两种设置 TTL 的方法,一是设置队列的 TTL,队列中的所有消息都有相同的过期时间,而就是对消息单独设置 TTL,每条消息的 TTL 可以不同。如果两种方式一起使用,则过期时间为两者的较小值。

先来看看如何对消息设置 TTL。

java 复制代码
public static final String TTL_EXCHANGE = "ttl.exchange";
public static final String TTL_QUEUE = "ttl.queue";
java 复制代码
@Bean("ttlExchange")
public DirectExchange ttlExchange() {
    return ExchangeBuilder.directExchange(Constants.TTL_EXCHANGE).durable(true).build();
}
    
@Bean("ttlQueue")
public Queue ttlQueue() {
    return QueueBuilder.durable(Constants.TTL_QUEUE).build();
}
    
@Bean("ttlBinding")
public Binding ttlBinding(@Qualifier("ttlQueue") Queue queue,@Qualifier("ttlExchange") DirectExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with("ttl");
}
java 复制代码
@RequestMapping("/ttl")
public String ttl() {
	//设置消息的过期时间
    MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
        @Override
        public Message postProcessMessage(Message message) throws AmqpException {
            message.getMessageProperties().setExpiration("10000");
            return message;
        }
    };
    rabbitTemplate.convertAndSend(Constants.TTL_EXCHANGE,"ttl","rabbitmq ttl",messagePostProcessor);
    return "消息发送成功";
}

这里消费者的代码我们就不写了,就通过 RabbitMQ 的管理页面观察消息的过期:

启动之后,并且让生产者生产消息,可以发现队列中已经存在一条消息了,然后等待一段时间再观察会发现刚刚生产的消息已经不存在了:

这是设置消息的过期时间,那么下面我们来看看如何设置队列的时间。

设置队列的 TTL

设置队列的 TTL 是在我们声明队列的时候设置的,声明队列的时候加入 x-message-ttl 参数实现的,单位是毫秒。

java 复制代码
//队列设置TTL的第一种方法
@Bean("ttlQueue")
public Queue ttlQueue() {
    return QueueBuilder.durable(Constants.TTL_QUEUE).ttl(20*1000).build();
}

//队列设置TTL的第二种方法
@Bean("ttlQueue")
public Queue ttlQueue() {
    Map<String, Object> arguments = new HashMap<>();
    arguments.put("x-message-ttl",20000);
    return QueueBuilder.durable(Constants.TTL_QUEUE).withArguments(arguments).build();
}

//也可以将map平铺出来
@Bean("ttlQueue")
public Queue ttlQueue() {
    Map<String, Object> arguments = new HashMap<>();
    arguments.put("x-message-ttl",20000);
    return QueueBuilder.durable(Constants.TTL_QUEUE).withArgument("x-message-ttl",20000).build();
}

这里创建两个队列,一个是设置了 TTL 的队列,一个是没有设置 TTL 的对了,然后看两个队列的差别:

java 复制代码
@Bean("ttlExchange")
public DirectExchange ttlExchange() {
    return ExchangeBuilder.directExchange(Constants.TTL_EXCHANGE).durable(true).build();
}

@Bean("ttlQueue")
public Queue ttlQueue() {
    Map<String, Object> arguments = new HashMap<>();
    arguments.put("x-message-ttl",20000);
    return QueueBuilder.durable(Constants.TTL_QUEUE).withArgument("x-message-ttl",20000).build();
}

@Bean("normalQueue")
public Queue normalQueue() {
    return QueueBuilder.durable(Constants.NORMAL_QUEUE).build();
}

@Bean("ttlBinding")
public Binding ttlBinding(@Qualifier("ttlQueue") Queue queue,@Qualifier("ttlExchange") DirectExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with("ttl");
}

@Bean("ttlBinding2")
public Binding ttlBinding2(@Qualifier("normalQueue") Queue queue,@Qualifier("ttlExchange") DirectExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with("ttl");
}


过一段时间之后再观察:

设置队列的过期时间和设置消息的过期时间的区别

设置队列TTL属性的方法,一旦消息过期,就会从队列中删除。

设置消息TTL的方法,即使消息过期,也不会马上从队列中删除,而是在即将投递到消费者之前进行判定的。

为什么这两种方法处理的方式不一样?

因为设置队列过期时间,队列中已过期的消息肯定在队列头部(假设是按照时间顺序入队的),RabbitMQ只要定期从队头开始扫描是否有过期的消息即可。

而设置消息TTL的方式,每条消息的过期时间不同,如果要删除所有过期消息需要扫描整个队列,这样效率较低。因此,RabbitMQ选择等到此消息即将被消费时再判定是否过期,如果过期再进行删除即可。跟 Redis 中的惰性删除是一个道理。这种方式避免了不必要的全队列扫描,提高了效率。

注意:这里的"即将投递到消费者之前"通常指的是在消息被发送到消费者之前,RabbitMQ会检查该消息是否已过期。如果消息已过期,RabbitMQ会将其删除,不会将其发送给消费者。这种机制确保了消费者不会接收到过期的消息。

相关推荐
ACP广源盛139246256732 小时前
此芯 P1 AI BOX / AI NAS@ACP#IX8008、IX8024 应用场景与市场机会
大数据·人工智能·分布式·单片机·嵌入式硬件
网络工程小王20 小时前
【HCIE-AI】12.deepspeed分布式并行训练进阶版
人工智能·pytorch·分布式·学习·昇腾·deepspeed
ACP广源盛139246256731 天前
国产算力互联IX8024@ACP#Kimi K3 开源后的端侧部署硬件架构分析
大数据·人工智能·分布式·单片机·嵌入式硬件
花生了什么事o1 天前
分布式 ID 生成方案:从数据库自增到雪花算法
数据库·分布式·算法
爱吃提升2 天前
python 分布式爬虫、爬虫合规与综合实战项目
分布式·爬虫·python
ZENERGY-众壹2 天前
跨品牌逆变器升级:华为锦浪德业 API 对接的 7 个坑
分布式·华为·数据归一化·光伏运维·逆变器api
薛定谔的猫19822 天前
LLaMA-Factory +DeepSpeed 分布式多卡训练实战全解
分布式·deepspeed·zero·多卡训练·多卡分布式训练
稚南城才子,乌衣巷风流2 天前
RabbitMQ 消息队列:从入门到实战
分布式·rabbitmq
Database_Cool_2 天前
单机 MySQL 迁移到分布式数据库方便吗?阿里云 PolarDB-X 100% MySQL 协议兼容零改造平滑迁移
数据库·分布式·mysql
肥胖小羊3 天前
解决企业微信 AccessToken 刷新并发冲突的分布式锁机制实践
分布式·企业微信