一、准备工作
本文演示的消息可靠性和消费端Ack机制是基于SpringBoot整合RabbitMQ来实现的,关于SpringBoot整合RabbitMQ可参考该链接:https://blog.csdn.net/pkxwyf/article/details/105158608
二、消息可靠性投递
在使用RabbitMQ时,消息发送方要尽可能保证任何消息在投递过程中不出现丢失。RabbitMQ为了帮助消息发送方尽可能避免消息丢失问题提供了两种方式用来控制消息的可靠性投递:confirm 确认模式 和 return 退回模式
RabbitMQ整个消息投递的路径为:Producer --> RabbitMQ broker --> exchange --> queue --> consumer
- 消息从producer 到 exchange 则会返回一个 confirmCallback
- 消息从 exchange 到 queue 投递失败会返回一个 returnCallback
2.1 Confirm 确认模式
1、在application.yml文件中开启确认模式
2、在rabbitTemplate定义ConfirmCallback回调函数
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRabbitMQ {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 消息可靠投递:确认模式
* 实现步骤:
* 1. 在application.yml文件中开启确认模式
* 2. 在rabbitTemplate定义ConfirmCallback回调函数
*/
@Test
public void testSendStr01(){
// 定义回调:监听消息确认信息
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* @param correlationData 相关配置信息
* @param ack exchange交换机是否成功收到了消息,true:成功,false:失败
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack){
System.out.println("交换机接收成功消息:" + cause);
} else {
System.out.println("交换机接收失败消息:" + cause);
}
}
});
// 发送消息
// 参数1:交换机名字
// 参数2:路由键
// 参数3:消息字符串
rabbitTemplate.convertAndSend(
"xkp_topic_exchange",
"xkp.news",
"hello rabbitmq......");
}
}
2.2 Return 退回模式
当消息发送给Exchange后,Exchange路由到Queue失败才会执行ReturnCallBack。
1、 在application.yml文件中开启 return 确认模式
2、在application.yml文件中配置 mandatory 属性
true: 消息路由不到Queue时通过ReturnCallBack回调函数返回给发送方。
false:消息没有路由到Queue,则丢弃消息(默认),也就触发不了ReturnCallBack回调函数执行了
3、在rabbitTemplate定义ReturnCallBack回调函数
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRabbitMQ {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 消息可靠投递:回退模式 ==> 当消息发送给Exchange后,Exchange路由到Queue失败才会执行ReturnCallBack
* 实现步骤:
* 1. 在application.yml文件中开启 return 确认模式
* 2. 在application.yml文件中配置 mandatory 属性
* 2.1 true: 消息路由不到Queue时通过ReturnCallBack回调函数返回给发送方。
* 2.2 false:消息没有路由到Queue,则丢弃消息(默认),也就触发不了ReturnCallBack回调函数执行了
* 3. 在rabbitTemplate定义ReturnCallBack回调函数
*/
@Test
public void testSendStr02(){
// 定义回调:监听消息回退
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/** 当消息没有路由到Queue时触发
* @param message 消息对象
* @param replyCode 错误码
* @param replyText 错误信息
* @param exchange 交换机
* @param routingKey 路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("return 执行了..........");
System.out.println("message = " + message);
System.out.println("replyCode = " + replyCode);
System.out.println("replyText = " + replyText);
System.out.println("exchange = " + exchange);
System.out.println("routingKey = " + routingKey);
}
});
// 发送消息
// 参数1:交换机名字
// 参数2:路由键
// 参数3:消息字符串
rabbitTemplate.convertAndSend(
"xkp_topic_exchange",
"xkp.news",
"hello rabbitmq......");
}
}
三、消费端Ack机制
1、消费端修改application.yml设置手动Ack
2、在监听方法中根据消息处理结果进行确认签收或拒绝签收
@Component
public class RabbitMQConfig {
/**
* 消费方手动 ack(确认)
* 实现步骤
* 1. 设置消费端手动ack,在application.yml文件设置:spring.rabbitmq.listener.simple.acknowledge-mode=manual
* 2. 如果消息成功处理,则调用channel的basicAck()方法确认签收
* 3. 如果消息处理失败,则调用channel的basicNack()方法拒绝签收,消息中间件broker会重新发送给消费方
*/
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "xkp_topic_exchange",type = "topic"),
value = @Queue(value = "xkp_queue",durable = "true"),
key = "xkp.#"
))
public void handlerMessage(Message message, Channel channel)throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
Thread.sleep(1000);
// 1. 转换消息
System.out.println("消息内容: " + new String(message.getBody()));
// 2. 业务处理
System.out.println("执行业务处理...");
// 模拟异常 System.out.println(1/0);
// 3. 手工签收
channel.basicAck(deliveryTag,true);
} catch (Exception e) {
// 4. 拒绝签收
// 参数3:true:重回队列,broker会重新发送该消息给消费端,false:不重回队列,消息会被丢弃或回到死信队列
channel.basicNack(deliveryTag,true,true);
}
}
}