rabbitmq动态监听浅记
浅析背景
使用rabbitmq时遇到需要进行队列拆分,比如拆成10个,my.queue.0-my.queue.9;消费者上需要完成对不同队列的消费,比如消费者1消费0.1队列,消费者消费2.3队列。
rabbitmq基础使用方式
基于spring-rabbitmq常见的使用方式是基于@RabbitListener完成RabbitListenerEndPoint定义及MessageListenerContainer的定义。基于这种使用方式不能满足动态监听的需要。所以选择模拟@RabbitListener的工作流程的方式实现,通过编码式生成RabbitListenerEndPoint,并注册到RabbitListenerEndPointRegistry上。
public void registerListenerContainer(RabbitListenerEndpoint endpoint, RabbitListenerContainerFactory<?> factory) {
this.registerListenerContainer(endpoint, factory, false);
}
手动注册可能存在的问题
这里描述的问题是由于项目已经通过@RabbitListener完成MessageListenerContainer的定义,通过RabbitListenerEndPointRegistry实现的LifeCycle接口在Spring容器启动的finishRefresh阶段完成LifeCycle初始化及start()的调用。而通过模拟@RabbitListener的工作流程中,通常会手动启动MessageListenerContainer,而@RabbitListener和模拟的MessageListenerContainer是注册到同一RabbitListenerEndPointRegistry上,由于手动启动MessageListenerContainer的原因而导致RabbitListenerEndPointRegistry提前处于isRunning的状态,引起后续LifeCycle执行阶段不执行LifeCycle的start()方法。这里可以查看DefaultLifeCycleProcessor的doStart()。
public boolean isRunning() {
Iterator var1 = this.getListenerContainers().iterator();
MessageListenerContainer listenerContainer;
do {
if (!var1.hasNext()) {
return false;
}
listenerContainer = (MessageListenerContainer)var1.next();
} while(!listenerContainer.isRunning());
return true;
}
只要任何一个MessageListenerContainer处于running中则RabbitListenerEndPointRegistry处于running状态
private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) {
Lifecycle bean = (Lifecycle)lifecycleBeans.remove(beanName);
if (bean != null && bean != this) {
String[] dependenciesForBean = this.getBeanFactory().getDependenciesForBean(beanName);
String[] var6 = dependenciesForBean;
int var7 = dependenciesForBean.length;
for(int var8 = 0; var8 < var7; ++var8) {
String dependency = var6[var8];
this.doStart(lifecycleBeans, dependency, autoStartupOnly);
}
if (!bean.isRunning() && (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle)bean).isAutoStartup())) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]");
}
try {
bean.start();
} catch (Throwable var10) {
throw new ApplicationContextException("Failed to start bean '" + beanName + "'", var10);
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Successfully started bean '" + beanName + "'");
}
}
}
}
可能的解决方式
方式一: 手动注册的RabbitListenerEndPoint autoStartUp设置为true,不手动启动
方式二(未确认):手动注册的RabbitListenerEndPoint autoStartUp还是设置成默认值false,应用启动过程中可以正常先启动@RabbitListener对应的MessageListenerContainer;然后通过实现Spring相关的接口如SpringApplicationRunListener对应的方法如running,在running中手动启动手动注册的MessageListenerContainer。