如何保证消息的可靠性
消息的可靠性需要两个方面来保证:
1. 生产者 -> 队列 (可靠投递)
2. 队列 -> 消费者 (可靠消费)
1. 可靠投递
生产者 -> 队列 需要经过两步:
1. 生产者 到 交换机
2. 交换机 到 队列
只要保证这两步的可靠传递就可以保证 消息的可靠投递了
1.1 生产者到交换机的可靠保证
生产者到交换机的可靠保证依靠的是: confrimCallback 机制。
confirmCallbake 翻译过来也就是确认回调。默认是不开启的
当开启这个模式之后,生产者个交换机发送一个消息,交换机收到这个消息之后,
会给消费者发送一个确认收到的信息。
在配置文件中开启 confirmCallback,这里采用yml文件
spring:
rabbitmq:
publisher-confirm-type: correlated # 开启confirmCallback
这是使用Junit4 测试方法发送一个消息进行测试
@Test
void testConfirmCallBack(){
// 设置ConfimCallball, 重写其中的 confim方法
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
/*
ack 表示消息是否到达了交换机,交换机是否确认收到消息
cause 存储的是没有到达交换机的原因
*/
if(ack==false){
System.out.println("如果没有到达交换机,根据需要写你的业务代码");
}
}
});
Map<String,Object> message=new HashMap<>();
message.put("product_id",10);
message.put("num",50);
message.put("price",600);
rabbitTemplate.convertAndSend("abc","hello.orange.abc", JSON.toJSONString(message));
}
1.2 交换机到队列的保证机制
采用 returnCallback 机制,默认不开启
returnCallback 翻译过来就是返回回调,但是这种回调机制并不像 confirmCallback 机制一样,
confirmCallback 不管消息是否成功到达交换机都会被调用,
returnCallback 只有在交换机到达队列失败的时候才会被触发,当这个回调函数被调用的时候,
说明交换机的消息没有顺利的到达队列
在配置文件中开启returnCallback 机制,这里使用yml文件
spring:
rabbitmq:
publisher-returns: true # 开启returnCallback
测试代码:
@Test
void testReturnCallBack(){
// 为rabbitTemplate设置return机制触发的方法
rabbitTemplate.setReturnsCallback(messgae->{
System.out.println(messgae);
});
Map<String,Object> message=new HashMap<>();
message.put("product_id",10);
message.put("num",50);
message.put("price",600);
rabbitTemplate.convertAndSend(
RabbitConfiguration.exchange_name01, // 交换机名称
"das", // routingKey
JSON.toJSONString(message));//要发送的消息
}
2. 保证消息到达消费者的可靠性
消息安全的到达消息队列之后,当它被消费者消费的时候,可能也会发生不可靠的传递。
还有就是之前采用的是自动确认模式,这时候如果消费者这边出现了一些问题,而消息队列那边以为消费者成功处理了,把消息给丢弃掉了,这时候也会有一些问题。这时候推荐使用手动确认模式
默认使用的是auto模式,需要配置使用手动确认
2.1 使用手动确认模式保证消费可靠
在配置文件中开启手动确认模式,这里使用yml文件
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual # 使用手动确认模式
测试代码
@Component
public class MyListener {
@RabbitListener(queues = {"springboot-topic-queue01"})
public void message(Message message, Channel channel) throws Exception{
//接受消息
System.out.println(message.getBody());
try {
System.out.println("业务处理");
// 没有出现异常,手动确认该消息,消息队列会删除该消息
channel.basicAck(
/// 获取消息的传送标签,queue使用该标签进行确认消息
message.getMessageProperties().getDeliveryTag(),
false // 是否把之前的消息全都确认。
);
}catch (Exception e){
// 如果出现异常,不确认该消息
channel.basicNack(
message.getMessageProperties().getDeliveryTag(),
false, // 是否确认多条
false // 是否重新发送
);
}
}
}
消息可靠性总结:
1. 持久化:
- exchange 持久化
- queue 持久化
- message 持久化
2. 生产者 -> 交换机 confirmCallback
3. 交换机 -> 队列 returnCallback
4. 队列 -> 消费者 手动确认
5. 还可以使用集群,防止Broker宕机
做到这些还是可能会有其他情况:
例如:消息的重复消费等
可以看这一篇:设计层面的消息可靠性保证:消息补偿、消息幂等性保障