赞
踩
场景:rabbitmq 服务器宕机时有消息正在处理,此时消息未处理完成,但是也没报错,再次启动MQ该消息消失
解决办法:手动ACK
手动ACK:但我们的业务完成的时候手动告诉队列已经完成,如果没告诉队列,该消息会一直存放在队列中。
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
告诉队列已经消费完成。参数1 当前消息的索引位置,
第二个参数是否批量ACK该索引之前的消息
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
告诉队列消费失败
参数1 当前消息的索引位置
第二个参数是否批量ACK该索引之前的消息
第三个参数 是否重回队列
配置文件
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 消息被拒绝以后不会重新进入队列,超过最大重试次数会进入死信队列 acknowledge-mode: manual #默认auto #NONE 可以称之为自动回调,即使无响应或者发生异常均会通知队列消费成功,会丢失数据。 #AUTO 自动检测异常或者超时事件,如果发生则返回noack,消息自动回到队尾,但是这种方式可能出现消息体本身有问题,返回队尾其他队列也不能消费,造成队列阻塞。 #MANUAL 手动回调,在程序中我们可以对消息异常记性捕获,如果出现消息体格式错误问题,手动回复ack,接着再次调用发送接口把消息推到队尾。
然后我们改一下消费者代码
@Component public class DelListener { @RabbitListener(queues = "cme_UploadZJFace") public void listener(Message message, Channel channel) throws IOException { SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd hh mm ss"); String s = sd.format(new Date()).toString(); System.out.println("消费中---------------------------"+s); try { int i=1/0; //channel.basicAck(message.getMessageProperties().getDeliveryTag(),false); } catch (Exception e) { System.out.println("消费失败---------------------------"+s); //channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false); e.printStackTrace(); } } @RabbitListener(queues = "REDIRECT_QUEUE") public void receiveA(Message message){ System.out.println("进入死信队列"); } }
发送一条消息发现
然后看一下MQ管理页面
我们发现有一条消息一直在队列里。
然后我们去掉ACK和NACK的注释。再次启动项目。
我们会发现在NACK以后这条消息会直接进入死信队列。
不会在进行重试。
再看一下管理页面
然后我们在试一下cath其他异常信息
换成IO异常
@RabbitListener(queues = "cme_UploadZJFace")
public void listener(Message message, Channel channel) throws IOException {
SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd hh mm ss");
String s = sd.format(new Date()).toString();
System.out.println("消费中---------------------------"+s);
try {
int i=1/0;
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (IOException e) {
System.out.println("消费失败---------------------------"+s);
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
e.printStackTrace();
}
}
控制台信息发现没有会重试5次,但是没有进入死信队列。
结果证明:如果我们配置了手动确认,那么我们只有在手动ACK才会确认消息,手动NACK才会进入死信队列。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。