spring webclient: retry with backoff on specific error
With reactor-extra you could do it like:
.retryWhen(Retry.onlyIf(this::is5xxServerError) .fixedBackoff(Duration.ofSeconds(10)) .retryMax(3))private boolean is5xxServerError(RetryContext<Object> retryContext) { return retryContext.exception() instanceof WebClientResponseException && ((WebClientResponseException) retryContext.exception()).getStatusCode().is5xxServerError();}
Update:With new API the same solution will be:
.retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(10)) .filter(this::is5xxServerError));//...private boolean is5xxServerError(Throwable throwable) { return throwable instanceof WebClientResponseException && ((WebClientResponseException) throwable).getStatusCode().is5xxServerError();}
You can do this taking the following approach:
- Use the
exchange()
method to obtain the response without an exception, and then throw a specific (custom) exception on a 5xx response (this differs fromretrieve()
which will always throwWebClientResponseException
with either a4xx
or5xx
status); - Intercept this specific exception in your retry logic;
- Use
reactor-extra
- it contains a nice way to useretryWhen()
for more complex & specific retries. You can then specify a random backoff retry that starts after 10 seconds, goes up to an arbitrary time and tries a maximum of 3 times. (Or you can use the other available methods to pick a different strategy of course.)
For example:
//...webclient.exchange().flatMap(clientResponse -> { if (clientResponse.statusCode().is5xxServerError()) { return Mono.error(new ServerErrorException()); } else { //Any further processing }}).retryWhen( Retry.anyOf(ServerErrorException.class) .randomBackoff(Duration.ofSeconds(10), Duration.ofHours(1)) .maxRetries(3) ));
the retryWhen with Retry.anyOf and Retry.onlyIf are deprecated I assume. I found this approach useful, and it allows us to process and throw a User defined exception.
for example :
retryWhen(Retry.backoff(3, Duration.of(2, ChronoUnit.SECONDS)) .filter(error -> error instanceof UserDefinedException/AnyOtherException) .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> new UserDefinedException(retrySignal.failure().getMessage())))