基于消息TTL与死信交换
死信交换: 可以为队列设置一个死信exchange和routingKey,当队列上产生死信时,死信会被投递到设置好的exchange及对应的routingKey,说白点就是可以为每个队列设置一个"回收站",产生死信就扔到回收站里,只不过回收站也是一个正常的队列.
消息成为死信的三中情况:
1.队列的消息长度达到限制
2.消费者拒接消费消息,basicNack/basicReject 并且不把消息重新放入原目标队列,requeue=false
3.原队列存在消息过期设置,消息到达超时时间未被消费
思路:
1.为消息设置过期时间
2.把消息投递到一个没有消费者的队列,ttl-queue 使消息成为死信
3.为ttl-queue设置死信交换,dead-letter-queue
4.为dead-letter-queue添加消费者,监听消息
引入rabbitMq 相关jar包
<!-- rabbitmq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.4.4</version>
</dependency>
先声明ttl 的交换机及队列;为没有消费者的队列,ttl-queue,绑定死信交换机,即为它指定"回收站"(死信交换)
@Configuration
public class TtlConfig {
@Bean("myTtlExchange")
public Exchange ttlExchange(){
return new DirectExchange("mine-ttl-exchange");
}
@Bean("myTtlQueue")
public Queue ttlQueue(){
//设置在这个队列上的私信交换
//mine-dead-letter-exchange 交换机
//并且routingkey为mine.dead.letter
Map<String,Object> args = new HashMap<>();
args.put("x-dead-letter-exchange","mine-dead-letter-exchange");
args.put("x-dead-letter-routing-key","mine.dead.letter");
//过期时间由队列统一设置
//注意不是 x-expires,x-expires为队列存活时间,
// x-message-ttl为队列内的消息存活时间
//注意更改队列/交换机设置需要删除原有的
// args.put("x-message-ttl",20000);
return new Queue("mine-ttl-queue",true,false,false,args);
}
@Bean
@DependsOn({"myTtlExchange","myTtlQueue"})
public Binding ttlBinding(Queue myTtlQueue, Exchange myTtlExchange){
return BindingBuilder.bind(myTtlQueue).to(myTtlExchange).with("mine.ttl").noargs();
}
}
监听上面设置好的死信队列
@Component
@RabbitListener(bindings = @QueueBinding(value = @Queue("mine-dead-letter-queue"),
exchange = @Exchange("mine-dead-letter-exchange"),
key = ("mine.dead.letter")))
public class DeadLetterListener {
@RabbitHandler
public void onMessage(String msg){
System.out.println(msg);
System.out.println(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
}
}
为消息设置过期时间
@GetMapping("testMq")
public void testMq(){
System.out.println(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
rabbitTemplate.convertAndSend("mine-ttl-exchange","mine.ttl","this is a rabbitmq test",message -> {
//设置10秒的过期时间
message.getMessageProperties().setExpiration("10000");
return message;
});
}
温馨提醒:每条消息都设置过期时间,很灵活,若是固定的过期时间,可以在队列上设置, 为队列设置 x-message-ttl 那在这个队列上的每条消息的过期时间都为 x-message-ttl 的值,如下面所示:
@Bean("myTtlQueue")
public Queue ttlQueue(){
//设置在这个队列上的私信交换(垃圾桶)
//mine-dead-letter-exchange 交换机
//并且routingkey为mine.dead.letter
Map<String,Object> args = new HashMap<>();
args.put("x-dead-letter-exchange","mine-dead-letter-exchange");
args.put("x-dead-letter-routing-key","mine.dead.letter");
//过期时间由队列统一设置
//注意不是 x-expires,x-expires为队列存活时间,
// x-message-ttl为队列内的消息存活时间
//注意更改队列/交换机设置需要删除原有的
args.put("x-message-ttl",20000);
return new Queue("mine-ttl-queue",true,false,false,args);
}
另:基于插件 RabbitMQ Delayed Message Plugin 个人推荐这篇文章 有兴趣的可以了解下https://blog.csdn.net/gw5205566/article/details/104594866