Only one ConfirmCallback is supported by each RabbitTemplate

在SpringBoot 整合 Rabbitmq时,我们开启消息发布者手动确认模式配置如下:

# 消息中间件
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    # 消息生产者确认模式
    publisher-confirm-type: correlated
     # 消息生产者回调开启
    publisher-returns: true

以上配置开启后我们需要在生产者端手动编写回调方式如下:

@Slf4j
@SpringBootTest
public class DHomeMessageApplicationTests {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void sendMessage(){
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(2000);
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                MessageDTO messageDTO = new MessageDTO();
                messageDTO.setTitle("hello rabbitmq 我是帅哥!!");
                messageDTO.setContext(format.format(new Date()));
                CorrelationData correlationData = new CorrelationData();
                String messageStr = JSONObject.toJSONString(messageDTO);
                ReturnedMessage returnedMessage = new ReturnedMessage(new Message(messageStr.getBytes()), 1, "1", MqConstants.EXCHANGE_NAME, "");
                correlationData.setReturned(returnedMessage);
                // MqConstants.EXCHANGE_NAME 交换机名称
                // MqConstants.QUEUE_NAME 队列名称
                rabbitTemplate.convertAndSend(MqConstants.EXCHANGE_NAME,MqConstants.QUEUE_NAME,messageDTO,correlationData);
                // 监听消息是否发送到交换机 回调
                rabbitTemplate.setConfirmCallback(((correlationData1, b, s) -> {
                    if(b){
                        log.error("发送消息成功");
                    }else {
                        log.error("发送消息到交换机失败");
                        ReturnedMessage returned = correlationData.getReturned();
                        Message message = returned.getMessage();
                        byte[] body = message.getBody();
                        String s1 = new String(body);

                        log.info("ConfirmCallback:     " + "相关数据:" + s1 );
                        log.info("ConfirmCallback:     " + "确认是否到达交换机:" + b);
                        log.info("ConfirmCallback:     " + "原因:" + s);
                    }
                }));
                // 消息发送到交换机成功,但是发送到队列失败 回调
                rabbitTemplate.setReturnsCallback(returnedMessage1 -> {
                    String s = returnedMessage1.getMessage().getBody().toString();
                    log.error("发送消息到队列失败");
                    log.error("消息内容:{}",s);
                });
            }catch (Exception e){
                log.error("发送消息失败");
                e.printStackTrace();
            }
        }
    }
}

以上代码执行时候会爆出错误:
在这里插入图片描述
以上报错原因是 rabbitMqTemplate 默认是单例的,一个rabbitMqTemplate 只能监听一个 ConfirCallback。网上的解决办法是:
修改 rabbitMqTemplate 为多例模式:如下: 我们定义一个多例模式的 rabbitMqTemplate 并在使用时注入自定义的多例Bean

    @Bean(name = "rabbitTemplatePrototype")
    //设置为多实例的,每次注入都使用不同的bean
    @Scope("prototype")
    public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        return rabbitTemplate;
    }

在生产者使用:

    @Resource(name = "rabbitTemplatePrototype")
    private RabbitTemplate rabbitTemplate;

再次运行: 使用了我们自定义的 多例模板之后,运行测试代码还是会报错如下:
在这里插入图片描述
这时候网上的文章又说 在Controller 中调用 并且指定这个Controller 也为多例的 ,我们做出修改:
在这里插入图片描述
以下是执行结果:
在这里插入图片描述
依旧是报错了,。。。。。。
暂时不管 报错,但是如果把消息发送的代码写在controller 中未免显得太过low (违反三层设计)。

一下提供可行的方案:
直接在定义rabbitMqTemplate 模板的Bean 中,设置回调方法如下:

    @Bean(name = "rabbitTemplate")
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 设置消息从生产者发送至 rabbitmq broker 成功的回调 (保证信息到达 broker)
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            // ack=true:消息成功发送到Exchange
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                ReturnedMessage returned = correlationData.getReturned();
                Message message = returned.getMessage();
                byte[] body = message.getBody();
                String s = new String(body);
                boolean valid = JSONObject.isValid(s);
                MessageDTO messageDTO=null;
                if(valid){
                     messageDTO = JSONObject.parseObject(s, MessageDTO.class);
                }
                log.info("ConfirmCallback:     " + "相关数据:" + messageDTO );
                log.info("ConfirmCallback:     " + "确认是否到达交换机:" + ack);
                log.info("ConfirmCallback:     " + "原因:" + cause);
            }
        });
        // 设置信息从交换机发送至 queue 失败的回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                log.info("ReturnCallback:     " + "消息:" + message);
                log.info("ReturnCallback:     " + "回应码:" + replyCode);
                log.info("ReturnCallback:     " + "回应信息:" + replyText);
                log.info("ReturnCallback:     " + "交换机:" + exchange);
                log.info("ReturnCallback:     " + "路由键:" + routingKey);
            }
        });
        // 为 true 时,消息通过交换器无法匹配到队列时会返回给生产者,为 false 时,匹配不到会直接丢弃
        rabbitTemplate.setMandatory(true);
        // 设置发送时的转换 序列化
        rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
        return rabbitTemplate;
    }

修改生产者代码,只需要发送消息即可:如下
在这里插入图片描述
运行结果为如下:并没有再次 爆出:Only one ConfirmCallback is supported by each RabbitTemplate 的问题
在这里插入图片描述
以上是演示 消息发送到交换机时交换机不存在报错,下面我们模拟 发送到交换机成功,发送到队列时候失败

在这里插入图片描述
在这里插入图片描述
消费者获取消息之后,确认已经发送到队列中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值