Search code examples
spring-bootspring-integration

RequestHandlerCircuitBreakerAdvice doesn't work if somethings fail in descending Service activator


I have problem with RequestHandlerCircuitBreakerAdvice.

In code below if RequestHandlerCircuitBreakerAdvice is added as advice in passThru and passThruSecond when something fails in JmsOutboundGateway (firstHandler or secondHandler) CircuitBreaker doesn't work.

But if I add RequestHandlerCircuitBreakerAdvice direclty in JmsOutboundGateway ( inside firstHandler or secondHandler)

 gateway.setAdviceChain(Collections.singletonList(requestHandlerCircuitBreakerAdvice));

and delete passThru and passThruSecond ServiceActivator with advice for CircuitBreaker.

It works fine.

Is it normal behavior that CircuitBreaker will only work when something fails directly in same ServiceActivator? or it should work even if something happens in descending ServiceActivator if I use same thread (direct channel)?

Below code doesn't work/use RequestHandlerCircuitBreakerAdvice :


    @MessagingGateway(name = "LocalGateway")
    public interface LocalGateway {
    
        @Gateway(requestChannel = "inputLocalChannel")
        ListenableFuture<Message<String>> sendMsg(Message<String> request);
    
    }


    @Bean
    public RequestHandlerCircuitBreakerAdvice requestHandlerCircuitBreakerAdvice(){
        RequestHandlerCircuitBreakerAdvice requestHandlerCircuitBreakerAdvice = new RequestHandlerCircuitBreakerAdvice();
        requestHandlerCircuitBreakerAdvice.setThreshold(2);
        requestHandlerCircuitBreakerAdvice.setHalfOpenAfter(20000);
        return  requestHandlerCircuitBreakerAdvice;
    }

    @Bean
    MessageChannel inputLocalGatewayChannel(){
        DirectChannel channel = new DirectChannel();
        return channel;
    }
    @Bean
    MessageChannel inputLocalSecondGatewayChannel(){
        DirectChannel channel = new DirectChannel();
        return channel;
    }

    @ServiceActivator(inputChannel = "inputLocalChannel", outputChannel = "inputLocalGatewayChannel", adviceChain = "requestHandlerCircuitBreakerAdvice")
    public Message passThru(Message message){
        return message;
    }

    @ServiceActivator(inputChannel = "inputLocalChannel", outputChannel = "inputLocalSecondGatewayChannel", adviceChain = "requestHandlerCircuitBreakerAdvice")
    public Message passThruSecond(Message message){
           return message;
       }
    
    @Bean
        @ServiceActivator(inputChannel = "inputLocalGatewayChannel")
        public JmsOutboundGateway firstHandler(){
        // some code
    }
    
    @Bean
        @ServiceActivator(inputChannel = "inputLocalSecondGatewayChannel")
        public JmsOutboundGateway secondHandler(){
        // some code
    }

Solution

  • It is called AbstractRequestHandlerAdvice for a reason.

    The logic there is like this:

    boolean isMessageMethod = (method.getName().equals("handleRequestMessage") || method.getName().equals("handleMessage"))
                && (arguments.length == 1 && arguments[0] instanceof Message);
    

    So, this AOP advice is applied only for a specific method in the MessageHandler implementation. It is not propagated downstream in the flow.

    You can try to wrap that one with a HandleMessageAdviceAdapter. See its Javadocs.

    Another solution is to extract your logic into a sub-flow and apply this RequestHandlerCircuitBreakerAdvice on the gateway() endpoint.

    See more in docs: https://docs.spring.io/spring-integration/reference/handler-advice.html