Rabbit消息可靠性投递
什么是消息的可靠性投递
- 保证消息百分百发送到消息队列中去
- 详细
- 保证mq节点成功接受消息
- 消息发送端需要接受到mq服务端接受到消息的确认应答
- 完善的消息补偿机制,发送失败的消息可以再感知并二次处理
RabbitMQ消息投递路径
- 生产者–>交换机->队列->消费者
- 通过两个的点控制消息的可靠性投递
- 生产者到交换机
- 通过confirmCallback
- 交换机到队列
- 通过returnCallback
- 生产者到交换机
注意:
开启消息确认机制以后,保证了消息的准确送达,但由于频繁的确认交互, rabbitmq 整体效率变低,吞吐量下降严重,不是非常重要的消息真心不建议用消息确认机制
生产者到交换机的可靠性投递confirmCallback
首先要在配置文件中添加 spring.rabbitmq.publisher-confirm-type: correlated 这个配置,开启confirmCallback功能
生产者测试代码
@Test
void testConfirmCallback(){
template.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*
* @param correlationData 配置
* @param ack 交换机是否收到消息,true是成功,false是失败
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("ConfirmCallback=======");
System.out.println("correlationData=======>"+correlationData);
System.out.println("ack=======>"+ack);
System.out.println("cause=======>"+cause);
if (ack) {
System.out.println("发送成功");
// TODO 更新数据库消息状态
}else {
System.out.println("发送失败,记录到日志或者数据库");
// TODO 更新数据库消息状态
}
}
});
// TODO 数据库新增一条记录,状态是发送
// 发送消息
// template.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"order.new","新订单" );
// 模拟异常
template.convertAndSend(RabbitMQConfig.EXCHANGE_NAME+"exTest","order.new","新订单" );
}
我做了一次异常测试,结果如下
ConfirmCallback=======
correlationData=======>null
ack=======>false
cause=======>channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'order_exchangeexTest' in vhost 'devTest', class-id=60, method-id=40)
发送失败,记录到日志或者数据库
cause字段给了异常原因,没有 'order_exchangeexTest’这个交换机。
交换机到队列可靠性投递returnCallback
交换机到队列
- 通过returnCallback
- 消息从交换器发送到对应队列失败时触发
- 两种模式
- 交换机到队列不成功,则丢弃消息(默认)
- 交换机到队列不成功,返回给消息生产者,触发returnCallback
配置文件设置
# 开启returnCallback
spring.rabbitmq.template.mandatory=true
#为true,则交换机处理消息到路由失败,则会返回给生产者
spring.rabbitmq.template.mandatory=true
测试代码:
@Test
void testReturnCallback(){
template.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
int replyCode = returnedMessage.getReplyCode();
String replyText = returnedMessage.getReplyText();
String returnedStr = returnedMessage.toString();
System.out.println("replyCode:::"+replyCode);
System.out.println("replyText:::"+replyText);
System.out.println("returnedStr:::"+returnedStr);
}
});
// 异常测试 将routingKey 改成asp.order.new
template.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"asp.order.new","新订单ReturnCallback" );
}
由于我之前在RabbitMQConfig配置类中 BindingBuilder.bind(queue).to(exchange).with("order.#").noargs();
这样把order.头的路由key都能正常接收,所以模拟异常的时候我在路由key的前面加上了asp让他进不去,出现异常
异常测试结果如下:
replyCode:::312
replyText:::NO_ROUTE
returnedStr:::ReturnedMessage [message=(Body:'新订单ReturnCallback' 。。。。。。
这里提示了没有这个路由。
消息确认机制ACK
RabbitMQ的ACK介绍
- 消费者从RabbitMQ收到消息并处理完成后,反馈给RabbitMQ,RabbitMQ收到反馈后才将此消息从队列中删除
- 消费者在处理消息出现了网络不稳定、服务器异常等现象,那么就不会有ACK反馈,RabbitMQ会认为这个消息没有正常消费,会将消息重新放入队列中
- 只有当消费者正确发送ACK反馈,RabbitMQ确认收到后,消息才会从RabbitMQ服务器的数据中删除。
- 消息的ACK确认机制默认是打开的,消息如未被进行ACK的消息确认机制,这条消息被锁定Unacked
确认方式
自动确认(默认)
手动确认manual
添加配置
spring.rabbitmq.listener.simple.acknowledge-mode: manual
消息监听类 OrderMQListener类代码
@Component
@RabbitListener(queues = "order_queue")
public class OrderMQListener {
@RabbitHandler
public void messageHandler(String body, Message message, Channel channel) throws IOException {
long msgTag = message.getMessageProperties().getDeliveryTag();
System.out.println("msgTag="+msgTag);
System.out.println("message="+message.toString());
System.out.println("body="+body);
// 告诉broker,消息已被确认
//channel.basicAck(msgTag,false);
// 告诉broker,消息拒绝确认
channel.basicNack(msgTag,false,true);
}
}
我这里做了拒绝测试,消息不断重新放入队列再次进行消费,测试结果如下:

开启服务的一秒就有上千次重新放回队列重新消费,我这里是因为没对消息做任何正常处理直接拒绝才会这样。
-
前面的Tag表示消息投递序号,每次消费消息或者消息重新投递后, deliveryTag都会增加
-
basicNack和basicReject介绍
- basicReject一次只能拒绝接收一个消息,可以设置是否requeue。
- basicNack方法可以支持一次0个或多个消息的拒收,可以设置是否requeue。
-
人工审核异常消息
- 设置重试阈值,超过后确认消费成功,记录消息,人工处理
本文详细介绍了RabbitMQ中确保消息可靠投递的机制,包括生产者到交换机的confirmCallback、交换机到队列的returnCallback以及消费者端的ACK确认。通过设置回调函数和配置参数,实现消息发送失败的记录与补偿,同时讨论了消息确认机制对系统性能的影响。此外,还展示了如何处理异常情况,并提供了相应的测试示例代码。
1670

被折叠的 条评论
为什么被折叠?



