1

I am trying to use the spring cloud resilience4j library to implement a circuit breaker for when an vendor api returns 500 errors or when it times out, the api is called using AsyncHttpClient. The problem seems to be that the circuit breaker is never opened and the fallback method is never executed when the API is returning 500 errors.

Could it be due to the fact I am overriding the onFailure (do this to capture metrics)

The factory is declared as follows:

@Autowired
private CircuitBreakerFactory circuitBreakerFactory;

The factory config is defined as follows:

@Bean
public Customizer<Resilience4JCircuitBreakerFactory> specificCustomConfiguration1() {

    TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
            .timeoutDuration(Duration.ofSeconds(2))
            .build();
    CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofMillis(1000))
            .slidingWindowSize(2)
            .build();

    return factory -> factory.configure(builder -> builder.circuitBreakerConfig(circuitBreakerConfig)
            .timeLimiterConfig(timeLimiterConfig).build(), "circuitBreaker");
}

The API is called with the following method, which also has the circuit breaker and .run method:

private void getRecommendationsAsync(final GatewayRecommendationsRequest request,
                                     final String variantName,
                                     final ThinkCallbackHandler callbackHandler)
        throws URISyntaxException, JsonProcessingException {

    CircuitBreaker circuitBreaker = circuitBreakerFactory.create("circuitBreaker");



    final AsyncHttpClient.BoundRequestBuilder builder = requestBuilder
            .prepareRequest(asyncHttpClient, request, variantName);


    circuitBreaker.run(() -> builder.execute(new AsyncCompletionHandler<Response>() {
        @Override
        public Response onCompleted(final Response response)
                throws Exception {
            processResponseStatus(response, variantName);

            GatewayRecommendations gatewayRecommendations = ((ThinkResponseMapper) responseMapper).map(request,
                    response,
                    useCaseId,
                    variantName);
            validator.validateResponse("/v1/recommendations", Request.Method.GET, response, gatewayRecommendations);
            callbackHandler.onSuccess(gatewayRecommendations);
            return response;
        }

        @Override
        public void onThrowable(final Throwable t) {
            callbackHandler.onFailure(t, recommendationsUri + " " + parameters.toString(), variantName);
        }


    }), throwable -> fallback());

}

and finally here is the fallback method i would like to invoke:

public String fallback() {
    LOGGER.warn("The fallback method is being used");
    System.out.println("Fallback Method is being executed");
    return "The circuit breaker is open";
}
user130316
  • 35
  • 1
  • 11

1 Answers1

1

Are you using AsyncHttpClient?

You could give our Resilience4j Spring Boot 2 Starter a try. It provides annotation support, external configuration, metrics, retry and many more features.

If you could return a CompletableFuture, it could look as follows:

@CircuitBreaker(name = "circuitBreaker", fallbackMethod="fallback")
public CompletableFuture<GatewayRecommendations> getRecommendationsAsync(final GatewayRecommendationsRequest request,
                                     final String variantName,
                                     final ThinkCallbackHandler callbackHandler)
        throws URISyntaxException, JsonProcessingException {

    final AsyncHttpClient.BoundRequestBuilder builder = requestBuilder
            .prepareRequest(asyncHttpClient, request, variantName);

    return builder.execute().toCompletableFuture()            
            .exceptionally(t -> { /* Something wrong happened... */  } )
            .thenApply(response -> { /*  Validate response and extract GatewayRecommendations  */  
   });


}

public CompletableFuture<GatewayRecommendations> fallback(RequestNotPermitted ex) {
    // The circuit breaker is open
    // Return a static list of recommendations
    return CompletableFuture.completedFuture(..)
}
Robert Winkler
  • 1,734
  • 9
  • 8
  • Hi Robert, thanks for getting back. Yes I realised that the return type and execute methid was the issue. No its the com.ning.http.client.AsyncHttpClient version which unfortunately doesnt have the to Complete-able future method. Will have a look at this and maybe rewriting the service to a Reactive model. Thanks Zain – user130316 Oct 23 '20 at 11:29
  • If the answer was still helpful, please accept it ;) – Robert Winkler Oct 26 '20 at 09:33
  • Sure. Moving to reactive will use a reactive CB, Thanks Robert :) – user130316 Oct 26 '20 at 11:50