0

I have a (legacy) TCP service that has multiple processes. Each process runs on the same host, but on a different port. The service is single threaded, so the way to increase throughput is to round-robin each request across each of the ports.

I am providing an AMQP exposure to this legacy application. Its very simple - take a string off the AMQP queue, pass it to the application, and return the response string to the AMQP reply queue.

This works great on a single port. However, i'd like to fan out the requests across all the ports.

Spring Integration seems to only provide AbstractClientConnectionFactory implementations that either connect directly to a single host/port (TcpNetClientConnectionFactory) or maintain a pool of connections to a single host/port (CachingClientConnectionFactory). There arent any that pool connections between a single host and multiple ports.

I have attempted to write my own AbstractClientConnectionFactory that maintains a pool of AbstractClientConnectionFactory objects and round-robins between them. However, I have struck several issues to do with handing the TCP connections when the target service goes away or the network is interrupted that I have not been able to solve.

There is also the approach taken by this question: Spring Integration 4 - configuring a LoadBalancingStrategy in Java DSL but the solution to that was to hardcode the number of endpoints. In my case, the number of endpoints is only known at runtime and is a user-configurable setting.

So, basically I need to create a TcpOutboundGateway per port dynamically at runtime and somehow register it in my IntegrationFlow. I have attempted the following:

@Bean
public IntegrationFlow xmlQueryWorkerIntegrationFlow() {
    SimpleMessageListenerContainer inboundQueue = getMessageListenerContainer();

    DirectChannel rabbitReplyChannel = MessageChannels.direct().get();

    IntegrationFlowBuilder builder = IntegrationFlows
            .from(Amqp.inboundGateway(inboundQueue)
                      .replyChannel(rabbitReplyChannel))    
            /* SOMEHOW DO THE ROUND ROBIN HERE */
            //I have tried:
            .channel(handlerChannel()) //doesnt work, the gateways dont get started and the message doesnt get sent to the gateway
            //and I have also tried:
            .handle(gateway1)
            .handle(gateway2) //doesnt work, it chains the handlers instead of round-robining between them
            //
            .transform(new ObjectToStringTransformer())
            .channel(rabbitReplyChannel); 

    return builder.get();

}

@Bean
//my attempt at dynamically adding handlers to the same channel and load balancing between them
public DirectChannel handlerChannel() {
    DirectChannel channel = MessageChannels.direct().loadBalancer(new RoundRobinLoadBalancingStrategy()).get();
    for (AbstractClientConnectionFactory factory : generateConnections()) {
        channel.subscribe(generateTcpOutboundGateway(factory));
    }
    return channel;
}

Does anyone know how I can solve this problem?

Community
  • 1
  • 1
Erin Drummond
  • 5,347
  • 6
  • 35
  • 41

1 Answers1

0

See the dynamic ftp sample - in essence each outbound gateway goes in its own application context and the dynamic router routes to the appropriate channel (for which the outbound adapter is created on demand if necessary).

Although the sample uses XML, you can do the same thing with java configuration, or even with the Java DSL.

See my answer to a similar question for multiple IMAP mail adapters using Java configuration and then a follow-up question.

Community
  • 1
  • 1
Gary Russell
  • 166,535
  • 14
  • 146
  • 179