mkopylec / charon-spring-boot-starter

Reverse proxy implementation in form of a Spring Boot starter.
Apache License 2.0
240 stars 54 forks source link

Treat HTTP 5xx responses as failures in circuit breaker #83

Closed mkopylec closed 3 years ago

mkopylec commented 5 years ago

To implement the feature https://github.com/resilience4j/resilience4j/issues/384 needs to be implemented first.

RobWin commented 5 years ago

A quick solution for Spring MVC would be to create a custom decorator here:

https://github.com/mkopylec/charon-spring-boot-starter/blob/79f13c17ed5343705d3b97b0e3c7e93273c052a8/charon-spring-webmvc/src/main/java/com/github/mkopylec/charon/forwarding/interceptors/resilience/CircuitBreaker.java#L27

Custom decorator:

static Supplier<HttpResponse> decorateSupplier(CircuitBreaker circuitBreaker, Supplier<HttpResponse> supplier){
        return () -> {
            circuitBreaker.acquirePermission();
            long start = System.nanoTime();
            try {
                HttpResponse  response = supplier.get();
                long durationInNanos = System.nanoTime() - start;
                HttpStatus statusCode = response.getStatusCode();
                if(!statusCode.is5xxServerError()){
                    circuitBreaker.onSuccess(durationInNanos);
                    return returnValue;
                }else{
                    circuitBreaker.onError(new HttpServerErrorException(statusCode));
                    return returnValue;
                }
            } catch (Exception exception) {
                // Do not handle java.lang.Error
                long durationInNanos = System.nanoTime() - start;
                circuitBreaker.onError(durationInNanos, exception);
                throw exception;
            }
        };

A quick solution for Spring WebFlux would be to wrap a 5xx response into an Mono.error and then unwrap the response again.

return execution.execute(request)
      .onStatus(HttpStatus::is5xxServerError, response ->
                Mono.error(new HttpServerException(response))
     )
     .transform(of(circuitBreaker))
    .onErrorResume(CallNotPermittedException.class, ex -> just(executeFallback(ex)))
    .onErrorResume(HttpServerException.class, ex -> just(ex.getResponse())
mkopylec commented 5 years ago

I know I can do it on my own but I would rather not to have "hacks" like those in the code. I will continue to work on https://github.com/resilience4j/resilience4j/issues/384

mkopylec commented 3 years ago

Done in 4.8.0