配置文件
添加:
#确认消息已发送到队列(Queue)
publisher-returns: true
配置文件整体:
配置类代码
/**
* 配置类 发布确认
*/
@Configuration
public class ConfirmConfig {
//普通交换机
public static final String CONFIRM_EXCHANGE_NAME="confirm_exchange";
//普通队列
public static final String CONFIRM_QUEUE_NAME="confirm_queue";
//RoutingKey
public static final String CONFIRM_EXCHANGE_ROUTING_KEY="key1";
//声明普通交换机
@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 queueBindingExchange(@Qualifier("confirmQueue") Queue confirmQueue,
@Qualifier("confirmExchange")DirectExchange confirmExchange){
return BindingBuilder.bind(confirmQueue).to(confirmExchange).with(CONFIRM_EXCHANGE_ROUTING_KEY);
}
回调接口配置
/**
*回调接口
*/
@Slf4j
@Component
public class MyCallBack implements RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init(){
//内部接口注入类中
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnsCallback(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 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
String id = correlationData != null ? correlationData.getId() : "";
if(ack){
log.info("交换机已经收到id为:{}的消息",id);
}
else {
log.info("交换机未经收到id为:{}的消息,原因为:{}",id,cause);
}
}
//在消息传递过程中不可达目的地时将消息返回给生产者
//只有不可达目的地时才进行回退
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
log.error("消息被交换机:{}退回,路由key:{},退回原因:{}",
returnedMessage.getExchange(), returnedMessage.getRoutingKey(),returnedMessage.getReplyText());
}
}
生产者代码
这一次,在生产者代码中,我们发两次消息,第一次RoutingKey是正常情况(key1),第二次将RoutingKey改成错误的(key12),观察接收情况和回报信息
/**
* 开始发消息,消息确认
*/
@Slf4j
@RestController
@RequestMapping("confirm")
public class ProduceController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMessage/{message}")
public void sendMessage(@PathVariable String message){
CorrelationData correlationData1 = new CorrelationData("1");
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,
ConfirmConfig.CONFIRM_EXCHANGE_ROUTING_KEY,message,correlationData1);
log.info("发送的消息内容为:{}",message+"key1");
CorrelationData correlationData2 = new CorrelationData("2");
rabbitTemplate.convertAndSend(ConfirmConfig.CONFIRM_EXCHANGE_NAME,
ConfirmConfig.CONFIRM_EXCHANGE_ROUTING_KEY+"2",message,correlationData2);
log.info("发送的消息内容为:{}",message+"key12");
}
}
消费者代码
/**
* 接收消息
*/
@Slf4j
@Component
public class Consumer {
@RabbitListener(queues = ConfirmConfig.CONFIRM_QUEUE_NAME)
public void receiveConfirmMessage(Message message){
String msg = new String(message.getBody());
log.info("接受到的队列confirm.queue消息:{}",msg);
}
}
测试
根据日志可以看到:交换机都收到消息,但是key12路径被退回,消费者只消费了正确发出的消息
测试完成
对于这类问题,也可以用备份交换机解决
消息回报和备份交换机同时配置时,默认使用备份交换机(优先级高)
同系列文章
原理部分
MQ(消息队列)简介
RabbitMQ简介
RabbitMQ 四大核心概念及工作原理
操作部分
Windows版Docker安装RabbitMq
Maven整合RabbitMQ实现生产消费消息
SpringBoot整合RabbitMQ实现生产消费消息
RabbitMQ延迟队列及实战
RabbitMQ发布确认-交换机确认
RabbitMQ-备份交换机
RabbitMQ-优先级队列