1. 发布确认高级
生产环境中遇到一些不明原因,导致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);
}
}
}
- 通过开启消息的回调接口之后,就可以得到消息的具体结果,当服务器下线即服务宕机之后此时发现返回的结果是交换机未收到消息,此时就应该对消息进行处理,如持久化,重发等
- 当消息成功到达交换机,但是队列的routingKey不一致,如上面的消息生产者发送的消息,此时会发现交换机回调接口中确认消息已经接收到,但是消费者没有接收到信息,导致消息的丢失.
对于上面这个消息丢失的问题,就需要开启交换机的回退接口,当消息由于某些原因导致没有到达队列的时候,就需要对消息进行回退处理,返回到生产者,保证消息的可靠性,这个回退的前提是开启了消息的生产者确认机制,否则不会生效,同时还要在配置文件开启路由不可达回退消息:
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改成错误的,查看结果,可以发现当路由不可达时会出来回退接口,将消息退回到生产者