I'm attempting to create a library that leverages Spring Webclient that has common failure logic for all of our applications. There are 2 considerations - configurable retry logic and failover logic to secondary sites.
The failover needs to handle network exceptions and request exceptions with a specific status code, default >= 500.
Would anyone be able to sanity check the below?
i.e.
public Builder webClientBuilder() throws SSLException {
return WebClient.builder()
.baseUrl(baseUrl)
.filter((request, next) -> exchangeFilterFunction(request, next))
.clientConnector(createReactorClientHttpConnector());
}
private Mono<ClientResponse> exchangeFilterFunction(ClientRequest request, ExchangeFunction next) {
if (StringUtils.hasLength(failoverBaseUrl)) {
return nextExchangeWithRetry(request, next)
.onErrorResume(e -> {
if (isRetriableException(e)) {
String uri = request.url().toString().replace(baseUrl, failoverBaseUrl);
LOGGER.info("Attempting to call Failover Site: " + uri);
ClientRequest retryRequest = ClientRequest.from(request).url(URI.create(uri)).build();
return nextExchangeWithRetry(retryRequest, next);
}
return Mono.error(e);
});
} else {
LOGGER.debug("No Failover Configured");
return nextExchangeWithRetry(request, next);
}
}
private Mono<ClientResponse> nextExchangeWithRetry(ClientRequest request, ExchangeFunction next) {
return next.exchange(request)
.flatMap(clientResponse -> {
HttpStatus httpStatus = clientResponse.statusCode();
if (httpStatus.isError()) {
return clientResponse.createException().flatMap(Mono::error);
}
return Mono.just(clientResponse);
})
.retryWhen(retrySpec(request));
}
private boolean isRetriableException(Throwable throwable) {
if (nonRetriableExceptions != null && nonRetriableExceptions.contains(throwable.getClass())) {
return false;
}
if (throwable instanceof WebClientResponseException
&& isNotRetryHttpResponseCode(((WebClientResponseException) throwable).getRawStatusCode())) {
return false;
}
return true;
}