1. 消息确认机制
在使用RabbitMQ的时候,可能会出现消息丢失的情况,这个时候就需要使用消息确认机制来保证消息的可靠生产和可靠消费
生产端消息确认:
ConfirmCallback:消息从生产端到交换机触发该回调函数 (无论成功或失败)
ReturnsCallback:消息从交换机到到队列触发该回调函数(消息发送失败)消费端消息确认:
手动ACK进行确认
2. 生产端消息确认
2.1 配置文件
spring:
rabbitmq:
# 开启confirm消息发送回调函数
publisher-confirm-type: correlated
# 开启return确认模式
publisher-returns: true
2.2 代码示例
/**
* @author: mingan.xie
* @since: 2021/3/31
* @history: 1.2021/3/31 created by xma
*/
@Component
public class RabbitMQSendCallback implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {
@Resource
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void run() {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnsCallback(this);
}
/**
* 消息发动到交换机时的回调函数
*
* @param correlationData
* @param b 如果消息发送成功则为true, 反之为false
* @param s 消息体
*/
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
if (!b) {
System.out.println("消息发送成功");
} else {
System.out.println("消息发送失败,进行容错处理");
}
System.out.println("消息发动到交换机时的回调函数, ack:" + b + " 消息体:" + s);
}
/**
* 消息从交换机发送到队列时的回调函数
* 如果发送成功则不回调该函数, 反之则回调
*
* @param returnedMessage
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息从交换机发送到队列时的回调函数, 调用失败!!!");
}
}
3. 消费端消息确认
解决方案:
控制重发次数 + 死信队列处理 (注意需要抛出异常,不能try…catch,否则会陷入死循环)
try…catch + 手动Ack + 人工干预(示例为此种方案)
try…catch + 手动Ack + 死信队列
3.1 配置文件
spring:
rabbitmq:
listener:
simple:
prefetch: 1 #限制每次只发送一条消息,防止多个通道消息积压过多导致服务崩溃
acknowledge-mode: manual #采用手动应答方式
# etry:
# #重试次数5(重新投递次数)
# max-attempts: 5r
3.2 代码示例
/**
* @author: mingan.xie
* @since: 2021/3/29
* @history: 1.2021/3/29 created by xma
*/
@Component
@RabbitListener(queues = {"duanxin.direct.queue"})
public class DirectDuanxinConsumer {
@RabbitHandler
public void reviceMessage(String message, Channel channel, Message message1){
try {
System.out.println("direct Duanxin 接收到了消息:" + message);
// 手动ack, 进行消息确认机制
// deliveryTag:该消息的index
// multiple:是否批量. true:将一次性ack所有小于deliveryTag的消息
channel.basicAck(message1.getMessageProperties().getDeliveryTag(), false);
} catch (IOException e) {
/**
* 拒绝确认消息
* deliveryTag:该消息的index
* multiple:是否批量.true:将一次性拒绝所有小于deliveryTag的消息
* requeue:被拒绝的是否重新入队列
* true:重新进入队列并进行消费,可以配合自己定义的重试次数(非spring配置)进行处理
* false:丢弃该消息,可以配合死信队列进行处理
*/
try {
channel.basicNack(message1.getMessageProperties().getDeliveryTag(),false, false);
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
}
}
- 需要用到死信队列的可以查看这边博客 Spring Boot 整合RabbitMQ 配置文件方式