Spring Web络客户端:发生特定错误时回退重试

4urapxun  于 2022-11-21  发布在  Spring
关注(0)|答案(6)|浏览(154)

我想在等待10秒后重试请求3次,当响应是5xx.但我没有看到我可以使用的方法.在对象上

WebClient.builder()
                .baseUrl("...").build().post()
                .retrieve().bodyToMono(...)

我可以看到方法:
在重试计数但无延迟的条件下重试

.retry(3, {it is WebClientResponseException && it.statusCode.is5xxServerError} )

带回退和次数但无条件重试

.retryBackoff

还有一个retryWhen,但我不知道如何使用它

2mbi3lxu

2mbi3lxu1#

使用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();
}
mitkmikd

mitkmikd2#

您可以通过以下方法来实现此目的:

  • 使用exchange()方法获取没有异常的响应,然后在5xx响应上引发特定(自定义)异常(这与retrieve()不同,retrieve()总是引发4xx5xx状态的WebClientResponseException);
  • 在重试逻辑中拦截此特定异常;
  • 使用reactor-extra-它包含了一个很好的方式来使用retryWhen()进行更复杂和更具体的重试。然后你可以指定一个随机的回退重试,在10秒后开始,上升到任意时间,最多尝试3次。(当然,你也可以使用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)
    )
);
k75qkfdt

k75qkfdt3#

我认为不赞成使用带有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())))
gopyfrb3

gopyfrb34#

// ...
.retryWhen(
    backoff(maxAttempts, minBackoff)
        .filter(throwable -> ((WebClientResponseException) throwable).getStatusCode().is5xxServerError()))
// ...
wswtfjt7

wswtfjt75#

在现有代码中只添加withThrowable就可以让它工作。这对我来说很有效。你可以尝试这样做:
例如:

.retryWhen(withThrowable(Retry.any()
    .doOnRetry(e -> log
        .debug("Retrying to data for {} due to exception: {}", employeeId, e.exception().getMessage()))
    .retryMax(config.getServices().getRetryAttempts())
    .backoff(Backoff.fixed(Duration.ofSeconds(config.getServices().getRetryBackoffSeconds())))))
uxhixvfz

uxhixvfz6#

我是这么做的:

.retryWhen(retryBackoffSpec())

private RetryBackoffSpec retryBackoffSpec() {
        return Retry.backoff(RETRY_ATTEMPTS, Duration.ofSeconds(RETRY_DELAY))
                .filter(throwable -> throwable instanceof yourException);
    }

相关问题