在项目中使用到了rabbitmq,项目是基于springboot的,所以理所应当的使用了spring包装的rabbitmq工具。需要时要配置不同的数据源,分成相应的消费者从不同交换机下的不同队列进行消费。
当然由于平时使用rabbitmq都是单数据源,从网上借鉴了一些配置多数据源的demo,例如:
@Configuration
public class RabbitConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(RabbitConfiguration.class);
@Resource(name = "accountUnbindHost")
private Host accountUnbindHost;
@Resource(name = "cardUnbindHost")
private Host cardUnbindHost;
@Bean(name = "accountUnbindConnectionFactory")
@Primary
public ConnectionFactory accountUnbindConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(accountUnbindHost.getAddresses());
connectionFactory.setUsername(accountUnbindHost.getUsername());
connectionFactory.setPassword(accountUnbindHost.getPassword());
connectionFactory.setVirtualHost(accountUnbindHost.getVirtualHost());
return connectionFactory;
}
@Bean(name = "accountUnbindListenerContainer")
@Primary
public SimpleRabbitListenerContainerFactory accountUnbindListenerContainer(
SimpleRabbitListenerContainerFactoryConfigurer configurer,
@Qualifier("accountUnbindConnectionFactory") ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
//
// @Bean(name = "accountUnbindRabbitTemplate")
// @Primary
// public RabbitTemplate accountUnbindrabbitTemplate(@Qualifier("accountUnbindConnectionFactory") ConnectionFactory connectionFactory) {
// return new RabbitTemplate(connectionFactory);
// }
/**
* 声明账户解绑交换机
*/
@Bean
FanoutExchange accountUnBindFanoutExchange() {
// return new FanoutExchange
return new FanoutExchange(CommonConstant.ACCOUNT_UBIND_FANOUT_EXCHANGE_NAME,true,false);
}
/**
* 声明账户解绑队列,扇形交换机会广播到每个绑定的队列
*/
@Bean
public Queue accountUnBindQueue() {
return new Queue(CommonConstant.ACCOUNT_UBIND_QUEUE_NAME, true);
}
//绑定 将队列和交换机绑定
@Bean
Binding bindingAccountUnBind() {
return BindingBuilder.bind(accountUnBindQueue()).to(accountUnBindFanoutExchange());
}
@Bean(name = "cardUnbindConnectionFactory")
public ConnectionFactory cardUnbindConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(cardUnbindHost.getAddresses());
connectionFactory.setUsername(cardUnbindHost.getUsername());
connectionFactory.setPassword(cardUnbindHost.getPassword());
connectionFactory.setVirtualHost(cardUnbindHost.getVirtualHost());
return connectionFactory;
}
@Bean(name = "cardUnbindListenerContainer")
public SimpleRabbitListenerContainerFactory cardUnbindistenerContainer(
SimpleRabbitListenerContainerFactoryConfigurer configurer,
@Qualifier("cardUnbindConnectionFactory") ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
@Bean(name = "cardUnbindRabbitTemplate")
public RabbitTemplate cardUnbindRabbitTemplate(@Qualifier("cardUnbindConnectionFactory") ConnectionFactory connectionFactory) {
return new RabbitTemplate(connectionFactory);
}
/**
* 声明银行卡解绑交换机
*/
@Bean
FanoutExchange cardUnbindFanoutExchange() {
// return new FanoutExchange
return new FanoutExchange(CommonConstant.CARD_UBIND_FANOUT_EXCHANGE_NAME,true,false);
}
/**
* 声明银行卡解绑队列,扇形交换机会广播到每个绑定的队列
*/
@Bean
public Queue cardUnbindQueue() {
return new Queue(CommonConstant.CARD_UBIND_QUEUE__NAME, true);
}
//绑定 将队列和交换机绑定
@Bean
Binding bindingCardUnbind() {
return BindingBuilder.bind(cardUnbindQueue()).to(cardUnbindFanoutExchange());
}
}
然而在使用的过程中并不顺利,不同的两个ip以及vhost,在第一个vhost竟然创建了第二个vhost需要的交换机以及队列。
在spring初始化的过程中,@Primary标注的类,会作为优先加载项,如下图所示,从RabbitAutoConfiguration进行初始化:
RabbitAdmin类实现了InitializingBean接口,因此在初始化完bean之后,会执行实现该接口中的afterPropertiesSet方法:
从该方法可以看出链接工厂实例connectionFactory会添加链接监听器,用于在链接建立完成之后,执行该监听方法:
该监听函数式接口实现:
connection -> {
if (!initializing.compareAndSet(false, true)) {
// If we are already initializing, we don't need to do it again...
return;
}
try {
/*
* ...but it is possible for this to happen twice in the same ConnectionFactory (if more than
* one concurrent Connection is allowed). It's idempotent, so no big deal (a bit of network
* chatter). In fact it might even be a good thing: exclusive queues only make sense if they are
* declared for every connection. If anyone has a problem with it: use auto-startup="false".
*/
if (this.retryTemplate != null) {
this.retryTemplate.execute(c -> {
initialize();
return null;
});
}
else {
initialize();
}
}
finally {
initializing.compareAndSet(true, false);
}
}
对于交换机以及队列的创建就是在该函数式方法中的initialize(),如下图所示:
在该方法中,会查询出spring管理的所有交换机、队列以及绑定,紧接着就是在信道中创建交换机、队列以及绑定,如下图:
只要是在文章初始中的RabbitConfiguration声明的交换机以及bean都会在该信道中创建,因此就导致了开头中的问题:
由于/cif的链接是是优先配置项,因此本不属于自己的bcm_x交换机也被创建。
那么何时会调用RabbitAutoConfiguration自动加载时的RabbitAdmin呢,上文提到了是在初始化完bean之后。怎么解决该问题呢?就是自己定义RabbitAdmin:
@Bean(name = "cardUnbindRabbitAdmin")
public RabbitAdmin cardUnbindRabbitAdmin(@Qualifier("cardUnbindConnectionFactory") ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
return rabbitAdmin;
}
@Bean(name = "cardUnbindRabbitTemplate")
public RabbitTemplate cardUnbindRabbitTemplate(@Qualifier("cardUnbindConnectionFactory") ConnectionFactory connectionFactory) {
return new RabbitTemplate(connectionFactory);
}
/**
* 声明银行卡解绑交换机
*/
@Bean
FanoutExchange cardUnbindFanoutExchange(@Qualifier("cardUnbindRabbitAdmin") RabbitAdmin rabbitAdmin) {
FanoutExchange fanoutExchange = new FanoutExchange(CommonConstant.CARD_UBIND_FANOUT_EXCHANGE_NAME,true,false);
fanoutExchange.setAdminsThatShouldDeclare(rabbitAdmin);
// return new FanoutExchange
return fanoutExchange;
}
/**
* 声明银行卡解绑队列,扇形交换机会广播到每个绑定的队列
*/
@Bean
public Queue cardUnbindQueue(@Qualifier("cardUnbindRabbitAdmin") RabbitAdmin rabbitAdmin) {
Queue queue = new Queue(CommonConstant.CARD_UBIND_QUEUE__NAME, true);
queue.setAdminsThatShouldDeclare(rabbitAdmin);
return queue;
}
//绑定 将队列和交换机绑定
@Bean
Binding bindingCardUnbind(@Qualifier("cardUnbindRabbitAdmin") RabbitAdmin rabbitAdmin) {
Binding binding = BindingBuilder.bind(cardUnbindQueue(rabbitAdmin)).to(cardUnbindFanoutExchange(rabbitAdmin));
binding.setAdminsThatShouldDeclare(rabbitAdmin);
return binding;
}
通过自定义的RabbitAdmin,setAdminsThatShouldDeclare表明交换机、队列以及绑定与该RabbitAdmin绑定,在initialize()方法中,下段代码会将不属于自己的交换机、队列以及绑定过滤掉,从而达到只会声明自己的配置。
spring rabbitmq的其他源码属实有点抽象,害,还得努力。