前言
最近遇到一个需求需要在项目中同时接收两个系统的消息,问题来了:
在A系统中消费B系统和C系统发过来的消息,但是
- B和C不在同一个vhost上,
- 甚至连访问的账号密码都不一样
该如何实现呢?答案是自定义ConnectionFactory和RabbitListenerContainerFactory
。
ContainerFactory
当我们使用@RabbitListener
注解注册监听器时,不知道你有没有发现一个额外的参数containerFactory
@RabbitListener(queues = "queue.orderStatus", containerFactory = "OrderContainerFacory")
这个containerFactory 填的是bean名称,指向一个containerFactory 对象,containerFactory 的作用是什么?
配置消费行为的逻辑,例如如下
- 指定连接工厂
- 指定消息内容序列化的方式
- 一次从mq中取出消息的数量
- 消息的确认模式
- 消费者线程的并发数
- 消息重试次数
正常我们不指定containerFacory时,SpringBoot会为我们自动创建一个SimpleRabbitListenerContainerFactory
,当我们要自定义ContainerFactory时,只需要将我们自定义的ContainerFactory注入到容器,此时Spring检查到项目中已有就不会自动创建,而是用我们的。
本文只是教大家如何使用,具体的原理就不在此处介绍,有兴趣可以参考org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
自定义CachingConnectionFactory
CachingConnectionFactory的作用是管理消费端与mq建立的连接,我们上面说的containerFacory也是基于此的基础来实现的。我
们要连接不同账号的VHost,那么就需要设置不同的CachingConnectionFactory。
如下,往容器中分别注入B系统和C系统的CachingConnectionFactory,
我们分别针对A系统和B系统单独设置了VHost,address,账号,密码参数
。
/**
* rabbitMQ配置
*
* @author wangmeng
* @since 2023/7/26
*/
@Configuration
@Slf4j
public class RabbitConfig {
@Autowired
RabbitProperties properties;
/**
* 指定连接为A系统
*
* @return mq连接
*/
@Bean("gigaCachingConnectionFactory")
@Primary
public CachingConnectionFactory gigaCachingConnectionFactory(@Value("${spring.rabbitmq.giga.virtual-host}") String vHost,
@Value("${spring.rabbitmq.giga.username}") String username,
@Value("${spring.rabbitmq.giga.password}") String password) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(properties.getAddresses());
connectionFactory.setPort(properties.getPort());
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(vHost);
return connectionFactory;
}
/**
* 指定连接为B系统
*
* @return mq连接
*/
@Bean
public CachingConnectionFactory xyCachingConnectionFactory(@Value("${spring.rabbitmq.xy.virtual-host}") String vHost,
@Value("${spring.rabbitmq.xy.username}") String username,
@Value("${spring.rabbitmq.xy.password}") String password) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(properties.getAddresses());
connectionFactory.setPort(properties.getPort());
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(vHost);
return connectionFactory;
}
}
自定义SimpleRabbitListenerContainerFactory
有了连接工厂后,就可以基于连接工厂来创建ContainerFactory,这里我们选择默认的实现SimpleRabbitListenerContainerFactory。
/**
* 配置A系统使用的containerFactory
*
* @param cachingConnectionFactory 连接配置
* @return A的containerFactory
*/
@Bean(name = "xyRabbitMqListenerContainerFactory")
public SimpleRabbitListenerContainerFactory xyRabbitMqFactory(@Qualifier("xyCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory) {
SimpleRabbitListenerContainerFactory listenerContainerFactory = new SimpleRabbitListenerContainerFactory();
listenerContainerFactory.setConnectionFactory(cachingConnectionFactory);
listenerContainerFactory.setMessageConverter(new SimpleMessageConverter());
// 手动确认
listenerContainerFactory.setAcknowledgeMode(properties.getListener().getSimple().getAcknowledgeMode());
listenerContainerFactory.setPrefetchCount(properties.getListener().getSimple().getPrefetch());
// 限制并发数为1
listenerContainerFactory.setConcurrentConsumers(1);
return listenerContainerFactory;
}
/**
* 配置B系统使用的containerFactory
*
* @param cachingConnectionFactory 连接配置
* @return A的containerFactory
*/
@Bean(name = "gigaRabbitMqListenerContainerFactory")
public SimpleRabbitListenerContainerFactory gigaRabbitMqFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer,
@Qualifier("gigaCachingConnectionFactory") CachingConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory listenerContainerFactory = new SimpleRabbitListenerContainerFactory();
listenerContainerFactory.setConnectionFactory(connectionFactory);
listenerContainerFactory.setMessageConverter(new SimpleMessageConverter());
// 手动确认
listenerContainerFactory.setAcknowledgeMode(properties.getListener().getSimple().getAcknowledgeMode());
listenerContainerFactory.setPrefetchCount(properties.getListener().getSimple().getPrefetch());
// 限制并发数为1
listenerContainerFactory.setConcurrentConsumers(1);
return listenerContainerFactory;
}
为队列绑定ContainerFactory
有了如上的配置,就可以为队列指定对应的containerFacory了
我们分别在两个消费者的注解里指定对应的containerFacory
@RabbitListener(queues = "queue.order", containerFactory = "xyRabbitMqListenerContainerFactory")
public class CustomsClearConsumer extends BaseConsumer {
// 省略业务代码……
}
@RabbitListener(queues = "queue.ship", containerFactory = "gigaRabbitMqListenerContainerFactory")
public class ShipConsumer extends BaseConsumer {
// 省略业务代码……
}