生产环境中遇到一些不明原因,导致rabbitmq宕机或者重启,导致生产者消息投递失败,导致消息丢失,需要手动恢复和重启,为了保证消息的安全性和可靠性,需要对发布确认做一定的处理,利用confirmCallBack接口来对消息确认的结果做处理,此时必须要将确认回调接口打开,因为发布确认模式默认是关闭的,需要手动打开:
spring.rabbitmq.publisher-confirm-type=correlated
创建发布确认配置类声明交换机和队列
@Configuration
public class ConfirmConfig {
public static final String CONFIRM_EXCHANGE_NAME = "confirm.exchange";
public static final String CONFIRM_QUEUE_NAME = "confirm.queue";
//声明业务 Exchange
@Bean("confirmExchange")
public DirectExchange confirmExchange(){
return new DirectExchange(CONFIRM_EXCHANGE_NAME);
}
// 声明确认队列
@Bean("confirmQueue")
public Queue confirmQueue(){
return QueueBuilder.durable(CONFIRM_QUEUE_NAME).build();
}
// 声明确认队列绑定关系
@Bean
public Binding queueBinding(@Qualifier("confirmQueue") Queue queue,
@Qualifier("confirmExchange") DirectExchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("key1");
} }
消息生产者中指定消息的id通过CorrelationData ,这个是消息回调接口中用来接收消息参数,是否确认成功,以及失败原因的接口,发送一条错误的信息,如指定routingKey的队列不存在或者rabbitmq宕机:
@GetMapping("/sendMsg/{msg}")
public void sendMsg(@PathVariable(value = "msg") String msg){
CorrelationData correlationData = new CorrelationData("1");
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE,"key2"
,msg,correlationData);
log.info("发送消息内容为: {}",msg);
}
此时如果没有开启回调接口,会发现消息已经丢失没有做处理,配置消息回调接口:
@Slf4j
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback , RabbitTemplate.ReturnsCallback {
public MyCallBack(RabbitTemplate rabbitTemplate) {
rabbitTemplate.setConfirmCallback(this);
}
/**
* 交换机确认回调方法
* 1. 交换机接收到了,回调
* 1.1 correlationData 保存回调消息的ID及相关信息
* 1.2 ack 交换机收到消息 true
* 1.3 cause 成功没有原因 null
* 2. 交换机没收到
* 2.1 correlationData 保存回调消息的ID及相关信息
* 2.2 ack 交换机没收到消息 false
* 2.3 cause 失败的原因
* @param correlationData 保存回调消息的ID及相关信息
* @param ack 交换机是否收到消息
* @param cause 失败的原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String id = correlationData != null ? correlationData.getId() : null;
if (ack){
log.info("交换机已经收到ID为: {}的消息",id);
}else {
log.info("交换机未收到ID为: {}的消息,原因为: {}",id,cause);
}
}
}
对于上面这个消息丢失的问题,就需要开启交换机的回退接口,当消息由于某些原因导致没有到达队列的时候,就需要对消息进行回退处理,返回到生产者,保证消息的可靠性,这个回退的前提是开启了消息的生产者确认机制,否则不会生效,同时还要在配置文件开启路由不可达回退消息:
spring:
rabbitmq:
host: 192.168.159.155
port: 5672
username: admin
password: 123456
publisher-confirm-type: correlated
publisher-returns: true #开启路由不可达回退消息
再在消息的发布确认回调接口中配置回退接口和处理,并注册到RabbitTemplate中,否则不会生效:
@Slf4j
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback , RabbitTemplate.ReturnsCallback {
public MyCallBack(RabbitTemplate rabbitTemplate) {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnsCallback(this);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String id = correlationData != null ? correlationData.getId() : null;
if (ack){
log.info("交换机已经收到ID为: {}的消息",id);
}else {
log.info("交换机未收到ID为: {}的消息,原因为: {}",id,cause);
}
}
/**
* 在消息路由不可达时将消息返回给生产者
* @param returned 回退消息
*/
@Override
public void returnedMessage(ReturnedMessage returned) {
log.error("消息{},被交换机{}退回,退回原因: {},路由key为:{}",returned.getMessage().toString(),returned.getExchange(),returned.getReplyText(),returned.getRoutingKey());
}
}
此时再进行生产,收到将routingKey改成错误的,查看结果,可以发现当路由不可达时会出来回退接口,将消息退回到生产者