Springboot集成多个RabbitMQ数据源创建队列混乱该怎么解决?

Springboot集成多个RabbitMQ数据源创建队列混乱该怎么解决?

背景

某服务配置了两个RabbitMQ数据源,并且在这两个数据源中分别建立一个exchange以及queue,但启动服务后发现所有的exchange和queue都被创建到某一个数据源中,服务启动失败。

问题代码

RabbitMQ配置类

java 复制代码
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class RabbitMQConfig {

    @Value("${spring.rabbitmq.concurrentConsumers}")
    int devAlarmConcurrentConsumers;
    @Value("${spring.rabbitmq.maxConcurrentConsumers}")
    int devAlarmMaxConcurrentConsumers;

    @Value("${spring.rabbitmq.addresses}")
    private String host;
    @Value("${spring.rabbitmq.port}")
    private int port;
    @Value("${spring.rabbitmq.username}")
    private String username;
    @Value("${spring.rabbitmq.password}")
    private String password;
    @Value("${spring.rabbitmq.virtual-host}")
    private String virtual_host;

    @Value("${spring.rabbitmq2.concurrentConsumers}")
    int concurrentConsumers2;
    @Value("${spring.rabbitmq2.maxConcurrentConsumers}")
    int maxConcurrentConsumers2;

    @Value("${spring.rabbitmq2.host}")
    private String host2;
    @Value("${spring.rabbitmq2.port}")
    private int port2;
    @Value("${spring.rabbitmq2.username}")
    private String username2;
    @Value("${spring.rabbitmq2.password}")
    private String password2;
    @Value("${spring.rabbitmq2.virtual-host}")
    private String virtual_host2;

    // ====================== 第一个mq数据源(主数据源)
    
    @Primary
    @Bean(name = "firstConnectionFactory")
    public ConnectionFactory firstConnectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(this.host);
        connectionFactory.setPort(this.port);
        connectionFactory.setUsername(this.username);
        connectionFactory.setPassword(this.password);
        connectionFactory.setVirtualHost(this.virtual_host);
        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);

        return connectionFactory;
    }

    @Bean(name = "firstRabbitTemplate")
    public RabbitTemplate firstRabbitTemplate(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory){
        RabbitTemplate rabbtiTemplate = new RabbitTemplate(connectionFactory);
        return rabbtiTemplate;
    }
    
    @Bean(name = "firstContainerFactory")
    public SimpleRabbitListenerContainerFactory firstContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer rabbitListenerContainerFactoryConfigurer,
                                                                                       @Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {

        SimpleRabbitListenerContainerFactory containerFactory=new SimpleRabbitListenerContainerFactory();
        rabbitListenerContainerFactoryConfigurer.configure(containerFactory,connectionFactory);
        containerFactory.setConcurrentConsumers(devAlarmConcurrentConsumers);
        containerFactory.setMaxConcurrentConsumers(devAlarmMaxConcurrentConsumers);
        return containerFactory;
    }

    // ====================== 第二个mq数据源

    @Bean(name = "secondConnectionFactory")
    public ConnectionFactory secondConnectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(this.host2);
        connectionFactory.setPort(this.port2);
        connectionFactory.setUsername(this.username2);
        connectionFactory.setPassword(this.password2);
        connectionFactory.setVirtualHost(this.virtual_host2);
        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);

        return connectionFactory;
    }
    
    @Bean(name = "secondRabbitTemplate")
    public RabbitTemplate secondRabbitTemplate(@Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory){
        RabbitTemplate rabbtiTemplate = new RabbitTemplate(connectionFactory);
        return rabbtiTemplate;
    }

    @Bean(name = "secondContainerFactory")
    public SimpleRabbitListenerContainerFactory secondContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer rabbitListenerContainerFactoryConfigurer,
        @Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory) {

        SimpleRabbitListenerContainerFactory containerFactory=new SimpleRabbitListenerContainerFactory();
        rabbitListenerContainerFactoryConfigurer.configure(containerFactory,connectionFactory);
        containerFactory.setConcurrentConsumers(concurrentConsumers2);
        containerFactory.setMaxConcurrentConsumers(maxConcurrentConsumers2);
        return containerFactory;
    }
}

消息监听类一

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class FirstReceiver {
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue("test.first.queue"),
        exchange = @Exchange(name = "test.first.exchange", type = ExchangeTypes.TOPIC),
        key = "test.first"
    ), containerFactory = "firstContainerFactory")
    @RabbitHandler
    public void handleMsg(Message message) {
        // 代码逻辑...
    }
}

消息监听类二

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SecondReceiver {
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue("test.second.queue"),
        exchange = @Exchange(name = "test.second.exchange", type = ExchangeTypes.FANOUT)
    ), containerFactory = "secondContainerFactory")
    @RabbitHandler
    public void handleMsg(Message message) {
        // 代码逻辑...
    }
}

追根溯源

有了当前错误现象,进行如下步骤的问题根源定位:

  1. 查看log日志,未发现异常
  2. 修改log日志级别为DEBUG,查看日志,如图:

如图所示,所有的exchange和queue都被同一个MQ数据源创建了。

  1. 查看源码

源码定位

发现【org.springframework.amqp.rabbit.core.RabbitAdmin】类有相关加载exchange和queue的逻辑,如图所示:

断点确认

在开发环境服务中打断点,发现,谁能创建这个exchange和queue的原则是,是否指定了exchange和queue是被那个MQ数据源绑定的,也就是需要用到【RabbitAdmin.java】实现【MQ数据源、exchange、queue】三者之间的绑定,如果没有定义这个RabbitAdmin,那就会默认都被MQ主数据源创建并绑定。

MQ主数据源:在一个集成了多个MQ数据源的Springboot项目中,必须要有一个被@Primary注解的主数据源,否则项目启动失败。

代码修改

RabbitMQ配置类

在原类基础上创建两个RabbitAdmin:

java 复制代码
@Configuration
public class RabbitMQConfig {

    @Bean(value = "firstRabbitAdmin")
    public RabbitAdmin firstRabbitAdmin(@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean(value = "secondRabbitAdmin")
    public RabbitAdmin secondRabbitAdmin(@Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }
}

消息监听类一

在原类的@RabbitListener注解中新增admins参数:

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class FirstReceiver {
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "test.first.queue",admins = "firstRabbitAdmin"),
        exchange = @Exchange(name = "test.first.exchange", type = ExchangeTypes.TOPIC,admins = "firstRabbitAdmin"),
        key = "test.first",
        admins = "firstRabbitAdmin"
    ), containerFactory = "firstContainerFactory")
    @RabbitHandler
    public void handleMsg(Message message) {
        // 代码逻辑...
    }
}

消息监听类二

在原类的@RabbitListener注解中新增admins参数:

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SecondReceiver {
    @RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "test.second.queue",admins = "secondRabbitAdmin"),
        exchange = @Exchange(name = "test.second.exchange", type = ExchangeTypes.FANOUT,admins = "secondRabbitAdmin"),
        admins = "secondRabbitAdmin"
    ), containerFactory = "secondContainerFactory")
    @RabbitHandler
    public void handleMsg(Message message) {
        // 代码逻辑...
    }
}

服务启动

最终两个MQ数据源可以分别创建对应的exchange和queue了。

相关推荐
Q_Q5110082854 小时前
python+django/flask的眼科患者随访管理系统 AI智能模型
spring boot·python·django·flask·node.js·php
韩立学长6 小时前
基于Springboot的旧时月历史论坛4099k6s9(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
Q_Q5110082856 小时前
python+django/flask的在线学习系统的设计与实现 积分兑换礼物
spring boot·python·django·flask·node.js·php
Q_Q5110082857 小时前
python+django/flask的车辆尾气检测排放系统-可视化大屏展示
spring boot·python·django·flask·node.js·php
汤姆yu7 小时前
基于SpringBoot的动漫周边商场系统的设计与开发
java·spring boot·后端
摇滚侠9 小时前
Spring Boot3零基础教程,响应式编程的模型,笔记109
java·spring boot·笔记
Q_Q19632884759 小时前
python+django/flask基于Echarts+Python的图书零售监测系统设计与实现(带大屏)
spring boot·python·django·flask·node.js·php
拾荒的小海螺10 小时前
JAVA:Spring Boot3 新特性解析的技术指南
java·开发语言·spring boot
鸽鸽程序猿11 小时前
【RabbitMQ】工作模式实现
分布式·rabbitmq
L.EscaRC11 小时前
Spring Boot 自定义组件深度解析
java·spring boot·后端