VHegde
VHegde

Reputation: 53

Resilience4J Circuit Breaker to kick-in on specific HTTP status code

I am aware we can use recordExceptions() while building CircuitBreakerConfig to register exceptions on which Circuit Breaker should transition to OPEN state.

Code

I am using resilience4j-feign to decorate my CircuitBreaker. Would be really helpful if you can point me to a code example.

Question

How to make the Circuit Breaker kick-in in case of a specific HTTP status code (e.g. on 503 Service Unavailable) ?

Upvotes: 2

Views: 17075

Answers (3)

hc_dev
hc_dev

Reputation: 9377

TL;DR: use a custom exception that communicates HTTP status (e.g. 503) from HTTP client (e.g. Feign) to Resilience4J

  1. Feign: implement and configure an ErrorDecoder to throw a custom exception on HTTP status like 503
  2. Resilience4J: record that custom exception using Circuit Breaker config.

Feign

Feign by default throws a FeignException in case of an erroneous HTTP status code. You can get the status code number via method int status().

To customize your feign-clients error-handling configure a (custom) implementation of ErrorDecoder

If you need more control over handling unexpected responses, Feign instances can register a custom ErrorDecoder via the builder. [..] All responses that result in an HTTP status not in the 2xx range will trigger the ErrorDecoder's decode method, allowing you to handle the response, wrap the failure into a custom exception or perform any additional processing. If you want to retry the request again, throw a RetryableException. This will invoke the registered Retryer.

Customize Feign error-handling

Implement and configure a custom ErrorDecoder to throw an exception in case of HTTP status 503.

@Component
@Slf4j
public class CustomErrorDecoder implements ErrorDecoder {
    
    @Override
    public Exception decode(String methodKey, Response response) {
        
        switch (response.status()) {
            case 400:
                log.error("Status code {} on methodKey '{}'", response.status(), methodKey);
            case 503:
                return new ServiceUnavailableException("HTTP status 503 when calling " methodKey);
            default:
                return new Exception(response.reason());
        } 
    }
    
}

This will then throw your custom exception ServiceUnavailableException.

Resilienc4J's CircuitBreaker

By default the circuit-breaker reacts on exceptions. It records them and will open the circuit if there are too much in too less time.

You can configure, which Exceptions to record and which to ignore as expected on the business-level.

Trigger CircuitBreaker on specific exceptions

You can configure CiruitBreaker to record that exception. Joke's answer explains how to do that.

See also

Upvotes: 2

Jocke
Jocke

Reputation: 2284

From the docs, Create and configure a CircuitBreaker:

// Create a custom configuration for a CircuitBreaker
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
  .recordExceptions(IOException.class, TimeoutException.class) // add you exceptions here!!!
  .ignoreExceptions(BusinessException.class, OtherBusinessException.class)
  .build();

A list of exceptions that are recorded as a failure and thus increase the failure rate. Any exception matching or inheriting from one of the list counts as a failure, unless explicitly ignored via ignoreExceptions.

Upvotes: 4

SHAKU
SHAKU

Reputation: 687

You need to write an Exception/Response handler to your client's external calls and throw custom exceptions based on http status received. Then register these exceptions as record exceptions in your circuit breaker config. Following is a small example. The CB will be open only on AbcException. The CB config isresilience4j.circuitbreaker.instances.bookService.record-exceptions=com.sk.example.cb.circuitbreakerr4j.AbcException

@Service
@Slf4j
public class BookApiService {
  RestTemplate restTemplate = new RestTemplate();
  @CircuitBreaker(name = "bookService", fallbackMethod = "getBookFallback")
  public String getBook(){
    try {
      ResponseEntity<String> stringResponseEntity = restTemplate.getForEntity(new URI("http://localhost:8080/book"), String.class);
      if(null != stringResponseEntity){
        if(stringResponseEntity.getStatusCode().is2xxSuccessful()){
          return stringResponseEntity.getBody();
        }
      }
    } catch (URISyntaxException e) {
      e.printStackTrace();
    }catch (HttpServerErrorException e){
      log.error("Service unavailable",e);
      if(e.getMessage().startsWith("503")) {
        throw new AbcException();
      }else{
        throw e;
      }
    }
    return "";
  }

Upvotes: 4

Related Questions