Spring集成Java DSL - 动态创建IntegrationFlows

时间:2018-05-17 14:23:14

标签: java spring spring-integration spring-integration-dsl spring-integration-amqp

我正在使用Spring Boot 1.5.13.RELEASE和Spring Integration 4.3.16.RELEASE开发应用程序。

我是Spring Integration的新手,我遇到了一个问题。

所以基本的想法是,在一些外部触发器(可能是和HTTP调用)上,我需要创建一个IntegrationFlow,它将使用来自rabbitMQ队列的消息,使用&em; em进行一些工作然后(可能)生成到另一个rabbitMQ端点。

现在这应该发生很多次,所以我必须创建多个 IntegrationFlows。

我正在使用 IntegrationFlowContext 来注册每个IntegrationFlow,如下所示:

IntegrationFlowContext flowContext;
...
IntegrationFlow integrationFlow = myFlowFactory.makeFlow(uuid);
...
flowContext.registration(integrationFlow).id(callUUID).register();

我必须澄清这可以同时发生,同时创建多个 IntegrationFlows。

因此,每当我尝试创建IntegrationFlow时,我的"来源"是一个看起来像这样的网关:

MessagingGatewaySupport sourceGateway = Amqp
        .inboundGateway(rabbitTemplate.getConnectionFactory(), rabbitTemplate, dynamicQueuePrefix+uuid)
        .concurrentConsumers(1)
        .adviceChain(retryInterceptor)
        .autoStartup(false)
        .id("sgX-" + uuid)
        .get();

它不是@Bean(还),但我希望在每个IntegrationFlow注册时都能注册。

我的目标"是一个AmqpOutBoundAdapter,如下所示:

@Bean
public AmqpOutboundEndpoint outboundAdapter(
        RabbitTemplate rabbitTemplate,
        ApplicationMessagingProperties applicationMessagingProperties
) {
    return Amqp.outboundAdapter(rabbitTemplate)
            .exchangeName("someStandardExchange")
            .routingKeyExpression("headers.get('rabbitmq.ROUTING_KEY')")
            .get();
}

现在这个 IS 已经是一个bean,并且每当我尝试创建流时都会注入它。

我的流程看起来像这样:

public IntegrationFlow configure() {
    return IntegrationFlows
            .from(sourceGateway)
            .transform(Transformers.fromJson(HashMap.class, jsonObjectMapper))
            .filter(injectedGenericSelectorFilter)
            .<HashMap<String, String>>handle((payload, headers) -> {

                String uuid = payload.get("uuid");

                boolean shouldForwardMessage = myInjectedApplicationService.isForForwarding(payload);
                myInjectedApplicationService.handlePayload(payload);

                return MessageBuilder
                        .withPayload(payload)
                        .setHeader("shouldForward", shouldForwardMessage)
                        .setHeader("rabbitmq.ROUTING_KEY", uuid)
                        .build();
            })
            .filter("headers.get('shouldForward').equals(true)")
            .transform(Transformers.toJson(jsonObjectMapper))
            .handle(outboundAdapter)
            .get();
}

我的问题是,当应用程序启动正常并创建第一个IntegrationFlows等。后来,我得到了这种例外:

  

java.lang.IllegalStateException:无法在bean名称下注册对象[org.springframework.integration.transformer.MessageTransformingHandler#872]&#39; org.springframework.integration.transformer.MessageTransformingHandler#872&#39;:有已经对象[org.springframework.integration.transformer.MessageTransformingHandler#872]绑定

我甚至尝试为每个使用的组件设置一个id,它应该被用作beanName,如下所示:

.transform(Transformers.fromJson(HashMap.class, jsonObjectMapper), tf -> tf.id("tf1-"+uuid))

但是,即使解决了.filter等组件的bean名称问题,我仍然会得到关于MessageTransformingHandler的相同异常。

更新

我没有提到这样一个事实:每次IntegrationFlow完成其工作后,就会使用IntegrationFlowContext这样删除它:

flowContext.remove(flowId);

所以似乎有某种功能是通过使用与锁相同的对象来同步流注册块和流删除块。

所以负责注册和删除流的我的类看起来像这样:

...
private final Object lockA = new Object();
...

public void appendNewFlow(String callUUID){
    IntegrationFlow integrationFlow = myFlowFactory.makeFlow(callUUID);

    synchronized (lockA) {
        flowContext.registration(integrationFlow).id(callUUID).register();
    }
}

public void removeFlow(String flowId){

    synchronized (lockA) {
        flowContext.remove(flowId); 
    }

}
...

我现在的问题是这种锁对于应用来说有点沉重,因为我得到了很多:

...Waiting for workers to finish.
...
...Successfully waited for workers to finish.

并不像我想的那样快。

但我想这是预期的,因为每次线程获得锁定时,注册流量及其所有组件或注销流程需要一些时间及其所有组成部分。

1 个答案:

答案 0 :(得分:0)

你也有这个:

df1.set_index('code').join(df2.set_index('sec.Code')).reset_index().fillna('Not match')

    code    name    age grade   sec.number
0   2877    Willard 20  88           10003
1   3000    Al      19  92           98822
2   3710    Omar    22  95       Not match
3   4001    Spencer 21  70       Not match
4   2338    Abin    18  76           11223

如果在那里添加.transform(Transformers.toJson(jsonObjectMapper)) ,它是如何工作的?

另一方面,既然你说这是同时发生的,我想知道你是否可以制作一些代码.id(),例如包裹synchonized

bean定义和注册过程实际上不是线程安全的,只能在应用程序生命周期开始时使用一个初始化线程。

我们可能真的需要在flowContext.registration(integrationFlow).id(callUUID).register();函数或至少它的IntegrationFlowContext中将register(IntegrationFlowRegistrationBuilder builder)设置为线程安全的,因为这正是我们生成bean名称和注册它。

随意就此事提出JIRA。

不幸的是,Spring Integration Java DSL扩展项目已经不再受支持,我们只能向当前registerBean(Object bean, String beanName, String parentName)代添加修复程序。不过我认为5.x解决方法应该在这里工作,因此无需将其移植到Spring Integration Java DSL扩展中。