假设一种场景:当我们生产者发送了错误的参数,或者消费者这边处理的时候抛出异常,然后这条消息会被重新放入队列中再次分发给消费者进行消费,这样子会造成一个死循环报错,十分影响性能和服务器资源。
简单的解决办法:直接try cath,报错以后消息直接丢失
比较好的解决办法:创建一个队列,用来处理抛出异常的消息。
// 首先声明死信队列
@Bean("redirectQueue")
public Queue redirectQueue() {
return QueueBuilder.durable("REDIRECT_QUEUE").build();
}
//KEY_R将 死信队列绑定到死信交换机
//注意 : 死信交换机是一个普通的交换机,可以是direct也可以是topic,也可以是fount
@Bean
public Binding redirectBinding() {
return new Binding("REDIRECT_QUEUE", Binding.DestinationType.QUEUE, "CME_EXCHANGE", "KEY_R", null);
}
//新建业务消息队列
@Bean(name = "cme_UploadHlJFace")
public Queue queue() {
Map<String, Object> args = new HashMap<>(2);
// x-dead-letter-exchange 声明 死信交换机
args.put("x-dead-letter-exchange", "CME_EXCHANGE");
// x-dead-letter-routing-key 声明 死信路由键
args.put("x-dead-letter-routing-key", "KEY_R");
return QueueBuilder.durable("cme_UploadHlJFace").withArguments(args).build();
}
//业务交换机
@Bean("CME_EXCHANGE")
public Exchange deadLetterExchange() {
return ExchangeBuilder.directExchange("CME_EXCHANGE").durable(true).build();
}
//业务交换机和队列绑定
@Bean
public Binding deadLetterBinding() {
return new Binding("cme_UploadHlJFace", Binding.DestinationType.QUEUE, "CME_EXCHANGE", "upload_key", null);
}
我们这边简单写一个消费者 做个实验
@Component
public class DelListener {
//监听业务队列
@RabbitListener(queues = "cme_UploadZJFace")
public void listener(Message message){
SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd hh mm ss");
String s = sd.format(new Date()).toString();
System.out.println("消费中---------------------------"+s);
int i=1/0;
}
//监听死信队列
@RabbitListener(queues = "REDIRECT_QUEUE")
public void receiveA(Message message){
System.out.println("进入死信队列");
}
}
消费者配置文件
server:
port: 8093
spring:
rabbitmq:
host: 192.168.1.36
port: 5672
username: cll
password: cll@2020
listener:
simple:
retry:
enabled: true
multiplier: 2 #乘子 默认是1
max-attempts: 5 #最大重试次数 默认是3
max-interval: 20000000 #最大重试间隔 默认是10000毫秒 也就是10s
initial-interval: 3000 # 间隔多少重试
default-requeue-rejected: false #false 消息被拒绝以后不会重新进入队列,超过最大重试次数会进入死信队列
然后生产者随便发送一条消息。
消费者会报错,看下控制台打印信息
重试5次进入死信队列
消息变成死信有以下几种情况:
-
消息被拒绝(basic.reject/ basic.nack)并且requeue=false
-
消息TTL过期(参考:RabbitMQ之TTL(Time-To-Live 过期时间))
-
队列达到最大长度