背景
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);
}
}