背景
flowable6.5后新增了事件注册引擎,对JMS,Apache Kafka和RabbitMQ具有开箱即用的支持。如果想支持其他的消息中间件也可通过添加自己的适配器来实现。
问题
SpringBoot项目中集成flowable6.5的事件注册引擎后,项目之前rabbitmq消费者不会自动开启。
项目之前配置有RabbitMQ的的消费者(其中autoStartup可以指定是否自动开启)
@Service
public class MQListener {
@RabbitListener(queues = "xxxx",autoStartup = "true")
public void handle(Channel channel, Message message) throws IOException {
...
}
}
部署channel、event到flowable的事件注册引擎。启动项目后发现MQListener配置的那些消费者不会启动。
问题跟踪
先阐述以下几个类的关系、作用
RabbitListener:加在方法上的注解,记录着一个消费者的相关信息(监听队列、是否自动开启、ack模式等)
RabbitListenerEndpoint:消费者终端,真正操作rabbitmq
MessageListenerContainer:管理RabbitListenerEndpoint的容器
RabbitListenerEndpointRegistry: 管理RabbitListenerEndpoint的注册器
他们之间的关系:RabbitListener ---根据配置创建→ RabbitListenerEndpoint ---注入→ MessageListenerContainer ---注入→ RabbitListenerEndpointRegistry
要开启消费需要调RabbitListenerEndpoint的start()方法,调MessageListenerContainer的start()会间接调RabbitListenerEndpoint的start()方法,而调RabbitListenerEndpointRegistry也会间接调MessageListenerContainer的start()方法
所以想要RabbitListenerEndpoint自动开启,一种方式是调RabbitListenerEndpointRegistry的start()方法。
在spring的AbstractApplicationContext类中,当走到finishRefresh()阶段时,就会从容器中取出所有实现了Lifecycle接口的bean,根据判断是否要调用start()方法。
而RabbitListenerEndpointRegistry是实现Lifecycle的接口的(通过SmartLifecycle间接实现),跟进去看到,只要有一个MessageListenerContainer是正在运行的话就会返回true, 这时!bean.isRunning()就是false了!
而一开始注册到RabbitListenerEndpointRegistry的MessageListenerContainer是不会开启的(具体可以看RabbitListenerEndpointRegistrar类的源码),但flowable的自动注入的RabbitChannelDefinitionProcessor类中,向RabbitListenerEndpointRegistry
注入的MessageListenerContainer是马上开启的,如下代码
解决方案
根据上面跟踪源码找到原因后,最后解决方案是注入一个名为rabbitChannelDefinitionProcessor的bean,继承RabbitChannelDefinitionProcessor,重写其中的一些方法,如下:
@Service("rabbitChannelDefinitionProcessor")
public class RabbitMQProcessor extends RabbitChannelDefinitionProcessor implements InitializingBean {
@Autowired
private RabbitListenerEndpointRegistry endpointRegistry;
@Autowired
private RabbitOperations rabbitOperations;
@Override
protected RabbitListenerEndpoint createRabbitListenerEndpoint(RabbitInboundChannelModel channelModel, String tenantId, EventRegistry eventRegistry) {
final SimpleRabbitListenerEndpoint endpoint = (SimpleRabbitListenerEndpoint) super.createRabbitListenerEndpoint(channelModel, tenantId, eventRegistry);
endpoint.setAutoStartup(true);
return endpoint;
}
@Override
protected void registerEndpoint(RabbitListenerEndpoint endpoint, RabbitListenerContainerFactory<?> factory) {
Assert.notNull(endpoint, "Endpoint must not be null");
Assert.hasText(endpoint.getId(), "Endpoint id must be set");
Assert.state(this.endpointRegistry != null, "No RabbitListenerEndpointRegistry set");
endpointRegistry.registerListenerContainer(endpoint, resolveContainerFactory(endpoint, factory));
}
@Override
public void afterPropertiesSet() throws Exception {
super.setEndpointRegistry(endpointRegistry);
super.setRabbitOperations(rabbitOperations);
}
}