Search code examples
javaspringspring-integration

Differences between Executor Channel and Direct Channel - Direct channel doubles messages in case of Failure but Executor channel don't in same case


I'm trying to find reason of different behavior between Executor Channel and Direct Channel in java app with Spring Integration framework.

I want to check failover flow and be sure that messages are not being duplicated - for both cases JmsOutboundGateway -> secondGateway will always throw error.

First scenario

inputLocalChannel - Executor Channel
inputGateway - Direct Channel
inputGatewayBck - Direct Channel ( secondGateway - failing flow )

I don't know why but messages are duplicated - I sent only 3 messages but on response queue there are 6

Second scenario

inputLocalChannel - Executor Channel
inputGateway - Executor Channel
inputGatewayBck - Executor Channel ( secondGateway - failing flow )

In this case I send 3 message and receive 3 - and this is as expected.

@MessagingGateway(name = "LocalGateway")
public interface LocalGateway {

    @Gateway(requestChannel = "inputLocalChannel")
    ListenableFuture<Message<String>> sendMsg(Message<String> request);

}

@Bean
    @ServiceActivator(inputChannel = "inputLocalChannel")
    public Message<String>firstHandler(){
    // some code
}

@Bean
    @ServiceActivator(inputChannel = "inputLocalChannel")
    public Message<String> secondHandler(){
    // some code
}


@Bean
    @ServiceActivator(inputChannel = "inputGateway")
    public JmsOutboundGateway firstGateway(){
    // some code
}

@Bean
    @ServiceActivator(inputChannel = "inputGatewayBck")
    public JmsOutboundGateway secondGateway(){
    // some code
}

Is there a possibility to use same thread in channel flow inputLocalChannel -> inputGateway inputLocalChannel -> inputGatewayBck

without context switching ? And why in case of using Direct Channel those messages are duplicated ?


Solution

  • The DirectChannel comes with a round-robin strategy by default. So, since you have two subscribers for that inputLocalChannel you end up with the situation when the first message is handled by the first subscriber, second by the second one and so on looping between those subscriber on each message.

    Now about errors. That channel uses a UnicastingDispatcher with a failover = true by default. And the logic is like this: if the current MessageHandler fails, the same message is handed to the next subscriber.

    Not sure if that is expected, but probably you would prefer to set LoadBalancingStrategy as null into that channel, so no round-robin would happen, but failover on the error in the MessageHandler would still be in action.

    The difference with an ExecutorChannel for that inputGateway that exception which happened in the subscriber for this channel is not bubbled up to the caller. Therefore that failover has no way to know that it has to iterate to the next subscriber. Just because all the stuff happens on a different thread and therefore error handling must be done differently: https://docs.spring.io/spring-integration/reference/error-handling.html