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了。

相关推荐
paopaokaka_luck42 分钟前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
P.H. Infinity3 小时前
【RabbitMQ】03-交换机
分布式·rabbitmq
小小小妮子~3 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616883 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
程序媛小果4 小时前
基于java+SpringBoot+Vue的旅游管理系统设计与实现
java·vue.js·spring boot
AskHarries6 小时前
Spring Boot集成Access DB实现数据导入和解析
java·spring boot·后端
2401_857622666 小时前
SpringBoot健身房管理:敏捷与自动化
spring boot·后端·自动化
程序员阿龙7 小时前
基于SpringBoot的医疗陪护系统设计与实现(源码+定制+开发)
java·spring boot·后端·医疗陪护管理平台·患者护理服务平台·医疗信息管理系统·患者陪护服务平台
前 方7 小时前
若依入门案例
java·spring boot·maven