簡體   English   中英

spring webclient:在特定錯誤時重試回退

[英]spring webclient: retry with backoff on specific error

當響應為 5xx 時,我想在等待 10 秒后重試請求 3 次。 但我沒有看到可以使用的方法。 在 object

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

我可以看到方法:

在有重試計數但沒有延遲的條件下重試

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

使用退避和次數重試,但沒有條件

.retryBackoff 

還有一個retryWhen但我不知道如何使用它

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

您可以通過以下方法做到這一點:

  • 使用exchange()方法獲取沒有異常的響應,然后在 5xx 響應上拋出特定(自定義)異常(這與retrieve()將始終拋出4xx5xx狀態的WebClientResponseException不同);
  • 在您的重試邏輯中攔截此特定異常;
  • 使用reactor-extra - 它包含一種使用retryWhen()進行更復雜和特定重試的好方法。 然后,您可以指定在 10 秒后開始的隨機退避重試,直至任意時間並嘗試最多 3 次。 (或者您當然可以使用其他可用的方法來選擇不同的策略。)

例如:

//...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)
    )
);

我認為不推薦使用帶有 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()))
// ...

僅將 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())))))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM