Reputation: 53
I am aware we can use recordExceptions()
while building CircuitBreakerConfig
to register exceptions on which Circuit Breaker should transition to OPEN
state.
I am using resilience4j-feign
to decorate my CircuitBreaker
. Would be really helpful if you can point me to a code example.
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
Reputation: 9377
TL;DR: use a custom exception that communicates HTTP status (e.g. 503) from HTTP client (e.g. Feign) to Resilience4J
ErrorDecoder
to throw a custom exception on HTTP status like 503Feign 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 theErrorDecoder
'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 aRetryableException
. This will invoke the registeredRetryer
.
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
.
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.
You can configure CiruitBreaker to record that exception. Joke's answer explains how to do that.
Upvotes: 2
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
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