spring webclient:重试特定错误退避
spring webclient: retry with backoff on specific error
当响应为 5xx 时,我想在等待 10 秒后重试请求 3 次。但我看不到可以使用的方法。在对象上
WebClient.builder()
.baseUrl("...").build().post()
.retrieve().bodyToMono(...)
我可以看到方法:
在有重试次数但没有延迟的条件下重试
.retry(3, {it is WebClientResponseException && it.statusCode.is5xxServerError} )
正在使用退避和次数重试,但没有条件
.retryBackoff
还有一个retryWhen
但是我不知道怎么用
您可以采用以下方法执行此操作:
- 使用
exchange()
方法获取没有异常的响应,然后在5xx响应上抛出一个特定的(自定义)异常(这不同于retrieve()
,后者总是抛出WebClientResponseException
状态为 4xx
或 5xx
);
- 在重试逻辑中拦截此特定异常;
- 当然可以使用
reactor-extra
- it contains a nice way to use retryWhen()
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 选择不同的策略。)
例如:
//...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)
)
);
使用 reactor-extra 你可以这样做:
.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();
}
更新:
使用新的 API 相同的解决方案将是:
.retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(10))
.filter(this::is5xxServerError));
//...
private boolean is5xxServerError(Throwable throwable) {
return throwable instanceof WebClientResponseException &&
((WebClientResponseException) throwable).getStatusCode().is5xxServerError();
}
我认为 Retry.anyOf 和 Retry.onlyIf 的 retryWhen 已被弃用。我发现这种方法很有用,它允许我们处理和抛出用户定义的异常。
例如:
retryWhen(Retry.backoff(3, Duration.of(2, ChronoUnit.SECONDS))
.filter(error -> error instanceof UserDefinedException/AnyOtherException)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) ->
new UserDefinedException(retrySignal.failure().getMessage())))
// ...
.retryWhen(
backoff(maxAttempts, minBackoff)
.filter(throwable -> ((WebClientResponseException) throwable).getStatusCode().is5xxServerError()))
// ...
当响应为 5xx 时,我想在等待 10 秒后重试请求 3 次。但我看不到可以使用的方法。在对象上
WebClient.builder()
.baseUrl("...").build().post()
.retrieve().bodyToMono(...)
我可以看到方法:
在有重试次数但没有延迟的条件下重试
.retry(3, {it is WebClientResponseException && it.statusCode.is5xxServerError} )
正在使用退避和次数重试,但没有条件
.retryBackoff
还有一个retryWhen
但是我不知道怎么用
您可以采用以下方法执行此操作:
- 使用
exchange()
方法获取没有异常的响应,然后在5xx响应上抛出一个特定的(自定义)异常(这不同于retrieve()
,后者总是抛出WebClientResponseException
状态为4xx
或5xx
); - 在重试逻辑中拦截此特定异常;
- 当然可以使用
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 选择不同的策略。)
例如:
//...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)
)
);
使用 reactor-extra 你可以这样做:
.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();
}
更新: 使用新的 API 相同的解决方案将是:
.retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(10))
.filter(this::is5xxServerError));
//...
private boolean is5xxServerError(Throwable throwable) {
return throwable instanceof WebClientResponseException &&
((WebClientResponseException) throwable).getStatusCode().is5xxServerError();
}
我认为 Retry.anyOf 和 Retry.onlyIf 的 retryWhen 已被弃用。我发现这种方法很有用,它允许我们处理和抛出用户定义的异常。
例如:
retryWhen(Retry.backoff(3, Duration.of(2, ChronoUnit.SECONDS))
.filter(error -> error instanceof UserDefinedException/AnyOtherException)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) ->
new UserDefinedException(retrySignal.failure().getMessage())))
// ...
.retryWhen(
backoff(maxAttempts, minBackoff)
.filter(throwable -> ((WebClientResponseException) throwable).getStatusCode().is5xxServerError()))
// ...