2
votes

We are developing a Spring Boot (2.4.0) application that uses Spring Integration framework(5.4.1) to build SOAP Integration flows and register them dynamically. The time taken to register ‘IntegrationFlow’ with ‘FlowContext’ is increasing exponentially as the number of flows being registered increase. Following is a quick snapshot of time taken to register flows:

5 flows – 500 ms 100 flows – 80 sec 300 flows – 300 sec

We see that first few flows are taking about 100ms to register, and as it reaches 300 it is taking up to 7 sec to register each flow. These flows are identical in nature (and they simply log an info message and return).

Any help to resolve this issue would be highly appreciated.

SoapFlowsAutoConfiguration.java (Auto Configuration class that registers Flows dynamically(manually))

 @Bean
  public UriEndpointMapping uriEndpointMapping(
      ServerProperties serverProps,
      WebServicesProperties webServiceProps,
      IntegrationFlowContext flowContext,
      FlowMetadataProvider flowMetadataProvider,
      @ErrorChannel(Usage.SOAP) Optional<MessageChannel> errorChannel,
      BeanFactory beanFactory) {

    UriEndpointMapping uriEndpointMapping = new UriEndpointMapping();
    uriEndpointMapping.setUsePath(true);

    Map<String, Object> endpointMap = new HashMap<>();
    flowMetadataProvider
        .flowMetadatas()
        .forEach(
            metadata -> {
              String contextPath = serverProps.getServlet().getContextPath();
              String soapPath = webServiceProps.getPath();
              String serviceId = metadata.id();
              String serviceVersion = metadata.version();

              String basePath = contextPath + soapPath;

              String endpointPath = String.join("/", basePath, serviceId, serviceVersion);

              SimpleWebServiceInboundGateway inboundGateway = new SimpleWebServiceInboundGateway();
              errorChannel.ifPresent(inboundGateway::setErrorChannel);
              endpointMap.put(endpointPath, inboundGateway);
              IntegrationFlowFactory flowFactory = beanFactory.getBean(metadata.flowFactoryClass());

              IntegrationFlow integrationFlow =
                  IntegrationFlows.from(inboundGateway).gateway(flowFactory.createFlow()).get();
              flowContext.registration(integrationFlow).register();
            });
    uriEndpointMapping.setEndpointMap(endpointMap);
    return uriEndpointMapping;
  }

SoapFlow.java (Integration Flow)

  @Autowired private SoapFlowResolver soapFlowResolver;
  @Autowired private CoreFlow delegate;

  @Override
  public IntegrationFlow createFlow() {
    IntegrationFlow a =
        flow -> flow.gateway(soapFlowResolver.resolveSoapFlow(delegate.createFlow()));
    return a;
  }

SoapFlowResolver.java (Common class used by all integration flows to delegate request to a Coreflow that is responsible for business logic implementation)

public IntegrationFlow resolveSoapFlow(
      IntegrationFlow coreFlow) {

    return flow -> {
      flow.gateway(coreFlow);
    };
  }

CoreFlow.java (Class that handles the business logic)

  @Override
  public IntegrationFlow createFlow() {

    return flow -> flow.logAndReply("Reached CoreFlow");
  }
1

1 Answers

2
votes

You are crating too many beans, where each of them checks the rest if it wasn't created before. That's how you get increase with the start time when you add more and more flows dynamically.

What I see is an abuse of the dynamic flows purpose. Each time we decide to go this way we need to think twice if we definitely need to have the whole flow as a fresh instance. Again: the flow is not volatile object, it registers a bunch of beans in the application context which are going to stay there until you remove them. And they are singletons, so can be reused in any other places of your application.

Another concern that you don't count with the best feature of Spring Integration MessageChannel pattern implementation. You definitely can have some common flows in advance and connect your dynamic with those through channel between them. You probably just need to create dynamically a SimpleWebServiceInboundGateway and wire it with the channel for your target logic which is the same for all the flows and so on.