Reputation: 883
I'm using Resilience4j to configure a circuit breaker and retry mechanism for my WebClient requests in a Spring Boot application. However, I'm facing two issues:
opens
for exceptions not specified in recordException
as well.Below is my WebClient code:
private TileResult getData(TileRequest tileRequest, String url) {
ResponseEntity<Flux<DataBuffer>> responseEntity = webClient.get().uri(url, tileRequest.getZ(), tileRequest.getX(), tileRequest.getY())
.retrieve()
.onStatus(status -> status == HttpStatus.NOT_FOUND,
error -> Mono.error(new ResourceNotFoundException("Tile not found")))
.onStatus(HttpStatusCode::is4xxClientError,
error -> {
log.error("Client error response from tile service API. Status code {}, Error {}", error.statusCode(), error.bodyToMono(String.class));
return Mono.error(new RemoteServiceException(error.statusCode().value(), "Error response from tile service API"));
})
.onStatus(HttpStatusCode::is5xxServerError,
error -> {
log.error("Server error response from tile service API. Status code {}, Error {}", error.statusCode(), error.bodyToMono(String.class));
return Mono.error(new WebClientServerException(error.statusCode().value(), "Error response from tile service API"));
})
.toEntityFlux(DataBuffer.class)
.transformDeferred(CircuitBreakerOperator.of(circuitBreaker))
.transformDeferred(RetryOperator.of(retry))
.block();
if (responseEntity != null && responseEntity.getStatusCode().is2xxSuccessful()) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
DataBufferUtils.write(responseEntity.getBody(), outputStream).blockLast();
return new TileResult(tileRequest, outputStream.toByteArray());
} catch (Exception e) {
log.error(withTileInfo("Error while downloading and saving tile data to cache", tileRequest, url), e);
throw new ServiceException("Error while downloading and saving tile data");
}
}
return null;
}
Here are my retry and circuit breaker configurations:
@Configuration
@Slf4j
public class ResilienceConfig {
public static final String CIRCUIT_BREAKER_CONFIG_NAME = "tileService";
public static final String RETRY_CONFIG_NAME = "tileService";
@Bean
public CircuitBreakerRegistry configureCircuitBreakerRegistry() {
final CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowSize(10)
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.minimumNumberOfCalls(4)
.failureRateThreshold(50)
.slowCallRateThreshold(100)
.slowCallDurationThreshold(Duration.ofMillis(30000))
.waitDurationInOpenState(Duration.ofMillis(10000))
.permittedNumberOfCallsInHalfOpenState(2)
.automaticTransitionFromOpenToHalfOpenEnabled(true)
.recordException(new RecordFailurePredicate())
.build();
return CircuitBreakerRegistry.of(Map.of(CIRCUIT_BREAKER_CONFIG_NAME, circuitBreakerConfig));
}
@Bean
public RetryRegistry configureRetryRegistry() {
final RetryConfig retryConfig = RetryConfig.custom()
.maxAttempts(3)
.intervalFunction(IntervalFunction.ofExponentialBackoff(IntervalFunction.DEFAULT_INITIAL_INTERVAL, 2))
.retryOnException(new RecordFailurePredicate())
.build();
return RetryRegistry.of(Map.of(RETRY_CONFIG_NAME, retryConfig));
}
}
class RecordFailurePredicate implements Predicate<Throwable> {
@Override
public boolean test(Throwable e) {
return recordFailures(e);
}
private boolean recordFailures(Throwable throwable) {
return throwable instanceof WebClientServerException ||
throwable instanceof TimeoutException || throwable instanceof IOException;
}
}
Could anyone help me understand what I might be missing?
Upvotes: 0
Views: 154