【spring boot rabbitmq系列二】多数据源

前言

之前在项目中使用了rabbitmq,然后简单整理了一下相关的信息,相关文章spring boot整合rabbitmq
后面由于需求改动,需要在一个项目中使用两个不同ip的数据源,接收不同的消息队列,因此这篇文章做一个简单记录。

准备

在之前的文章中,我们已经知道,spring boot整合rabbitmq,简单来说其实就是创建一个连接工厂(ConnectionFactory),然后在这个的基础上再做一些消费者,生产者等的创建绑定操作。这里有一篇spring boot 集成rabbitmq的源码分析。spring boot集成RabbitMQ 源码分析

那么,两个数据源,我创建两个ConnectionFactory,然后在这个基础上,再加拓展不就大功告成了?

一:创建两个连接工厂

@Bean(name = "firstConnectionFactory")
    public CachingConnectionFactory baseRabbitMqConnectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses("127.0.0.1:5672");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");
        connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL);
        connectionFactory.setChannelCacheSize(25);
        connectionFactory.setChannelCheckoutTimeout(0);
        connectionFactory.setPublisherReturns(false);
        connectionFactory.setPublisherConfirms(false);
        return connectionFactory;
    }
    
    @Bean(name = "secondConnectionFactory")
    public CachingConnectionFactory baseRabbitMqConnectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses("127.0.0.2:5672");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");
        connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL);
        connectionFactory.setChannelCacheSize(25);
        connectionFactory.setChannelCheckoutTimeout(0);
        connectionFactory.setPublisherReturns(false);
        connectionFactory.setPublisherConfirms(false);
        return connectionFactory;
    }

可以看到,上文我们创建了两个连接工厂,并且注册了bean。

二:绑定RabbitListenerContainerFactory

由于我们是使用@RabbitListener注解接收信息,每一个注解的方法都会由这个RabbitListenerContainerFactory创建一个MessageListenerContainer,负责接收消息。因此,我们需要将相应的连接绑定到这个类。同样,也是创建两个。

@Bean(name = "firstMultiListenerContainer")
    public SimpleRabbitListenerContainerFactory baseMultiListenerContainer(@Qualifier("firstConnectionFactory") ConnectionFactory firstConnectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(firstConnectionFactory);
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        factory.setConcurrentConsumers(5);
        factory.setMaxConcurrentConsumers(5);
        factory.setPrefetchCount(2);
        factory.setDefaultRequeueRejected(true);
        factory.setErrorHandler(new MqErrorHandler());
        return factory;
    }
    
    @Bean(name = "secondMultiListenerContainer")
    public SimpleRabbitListenerContainerFactory baseMultiListenerContainer(@Qualifier("secondConnectionFactory") ConnectionFactory secondConnectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(secondConnectionFactory);
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        factory.setConcurrentConsumers(5);
        factory.setMaxConcurrentConsumers(5);
        factory.setPrefetchCount(2);
        factory.setDefaultRequeueRejected(true);
        factory.setErrorHandler(new MqErrorHandler());
        return factory;
    }

这里由于有多个ConnectionFactory 的Bean,因此我们需要用到**@Qualifier**注解来指定方法引入哪一个bean。

三:消费者监听消息

@RabbitListener(queues = "queueName", containerFactory = "firstMultiListenerContainer", errorHandler = "rabbitListenerErrorHandler")
    public void handleMessage(Message msg, Channel channel) throws Exception{
        //业务逻辑
    }
    
    @RabbitListener(queues = "queueName", containerFactory = "secondMultiListenerContainer", errorHandler = "rabbitListenerErrorHandler")
    public void handleMessage1(Message msg, Channel channel) throws Exception{
        //业务逻辑
    }

看起来好像没什么问题,测试一下!

四:测试

***************************
    APPLICATION FAILED TO START
    ***************************
    
    Description:
    
    Parameter 1 of method simpleRabbitListenerContainerFactory in org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration required a single bean, but 2 were found:
    	- firstConnectionFactory: defined by method 'baseRabbitMqConnectionFactory' in class path resource [com/rabbitmq/sender/config/RabbitmqConfig.class]
    	- secondConnectionFactory: defined by method 'bizRabbitMqConnectionFactory' in class path resource [com/rabbitmq/sender/config/RabbitmqConfig.class]
    
    
    Action:
    
    Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

可以看到,应用启动失败,问题描述说RabbitAnnotationDrivenConfiguration 类的**simpleRabbitListenerContainerFactory **方法里面,有一个参数只需要一个bean,但是我们声明了两个,导致该方法不知道使用哪一个。下面来看一下这个方法:

simpleRabbitListenerContainerFactory方法

@Bean(
        name = {"rabbitListenerContainerFactory"}
    )
    @ConditionalOnMissingBean(
        name = {"rabbitListenerContainerFactory"}
    )
    @ConditionalOnProperty(
        prefix = "spring.rabbitmq.listener",
        name = {"type"},
        havingValue = "simple",
        matchIfMissing = true
    )
    public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        configurer.configure(factory, connectionFactory);
        return factory;
    }

可以看到,该方法是在没有**name=“rabbitListenerContainerFactory”**的Bean的时候,会调用这个方法创建一个Bean,方法里面的参数确实只需要一个ConnectionFactory 。

那么问题就来了,我们给它创建一个name=“rabbitListenerContainerFactory”的Bean,不让它调用这个方法不就行了?试试看!

方案一:创建一个name=“rabbitListenerContainerFactory”的Bean

@Bean(name = "rabbitListenerContainerFactory")
        public SimpleRabbitListenerContainerFactory baseMultiListenerContainer(@Qualifier("firstConnectionFactory") ConnectionFactory firstConnectionFactory){
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(firstConnectionFactory);
            factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
            factory.setConcurrentConsumers(5);
            factory.setMaxConcurrentConsumers(5);
            factory.setPrefetchCount(2);
            factory.setDefaultRequeueRejected(true);
            factory.setErrorHandler(new MqErrorHandler());
            return factory;
        }
    
        @Bean(name = "secondMultiListenerContainer")
        public SimpleRabbitListenerContainerFactory baseMultiListenerContainer(@Qualifier("secondConnectionFactory") ConnectionFactory secondConnectionFactory){
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(secondConnectionFactory);
            factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
            factory.setConcurrentConsumers(5);
            factory.setMaxConcurrentConsumers(5);
            factory.setPrefetchCount(2);
            factory.setDefaultRequeueRejected(true);
            factory.setErrorHandler(new MqErrorHandler());
            return factory;
        }

上面我们将第一个Bean的name改一下,再将对应的消费者监听信息改一下,再试一次!

    2019-07-17 09:36:56.698  INFO 11424 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [127.0.0.1:5672]
    2019-07-17 09:36:56.725  INFO 11424 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Created new connection: firstConnectionFactory#6d511b5f:0/SimpleConnection@4e868ef5 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 64602]
    2019-07-17 09:36:56.768  INFO 11424 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [127.0.1.1:5672]
    2019-07-17 09:36:56.772  INFO 11424 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Created new connection: secondConnectionFactory#1c807b1d:0/SimpleConnection@6872f9c8 [delegate=amqp://guest@127.0.1.1:5672/, localPort= 64603]

可以看到,这个真的是可以的!那么,还有其他方法吗?答案是肯定的!

方案二:使用@Primary

我们在问题的解决方案里,看到提示使用@Primary注解,该注解代表:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常。这样不就解决了刚才方法不知道使用哪个Bean的问题了。

    @Bean(name = "firstConnectionFactory")
    @Primary
    public CachingConnectionFactory baseRabbitMqConnectionFactory(){
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses("127.0.0.1:5672");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");
        connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CHANNEL);
        connectionFactory.setChannelCacheSize(25);
        connectionFactory.setChannelCheckoutTimeout(0);
        connectionFactory.setPublisherReturns(false);
        connectionFactory.setPublisherConfirms(false);
        return connectionFactory;
    }

我们将第一个Bean设置@Primary,再次启动!

2019-07-17 09:58:18.559  INFO 7276 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [127.0.1.1:5672]
    2019-07-17 09:58:18.585  INFO 7276 --- [           main] o.s.a.r.c.CachingConnectionFactory       : Created new connection: secondConnectionFactory#547e29a4:0/SimpleConnection@189b5fb1 [delegate=amqp://guest@127.0.1.1:5672/, localPort= 64874]
    2019-07-17 09:58:18.590  INFO 7276 --- [ntContainer#0-1] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [127.0.0.1:5672]
    2019-07-17 09:58:18.643  INFO 7276 --- [ntContainer#0-1] o.s.a.r.c.CachingConnectionFactory       : Created new connection: firstConnectionFactory#7a0e1b5e:0/SimpleConnection@48048382 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 64875]

可以看到,应用启动成功,该方法也可行!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值