使用来自 Spring WebFlux 的 webclient 在 Mono 上有条件地重复或重试

Conditional repeat or retry on Mono with webclient from Spring WebFlux

我想做的是在 Webflux 中的 Mono 上有条件地重复 webclient.The 情况如下:

我们有一些业务休息服务服务,returns 生成文档。此文档的生成是由在此之前调用的另一个服务触发的。但是,言归正传:文档生成服务需要 10-30 秒。我们要做的是:10 秒后检查是否生成了文档 (Mono)。如果是这样,一切都很好。如果没有,再过 5 秒后重复(或重试)并检查是否生成了文档。依此类推,直到(最坏的情况)30 秒后超时。这可能吗?一些(伪)代码:

return this.webClient.post().uri(SERVICE_URL)).        
body(BodyInserters.fromObject(docRequest)).retrieve().
bodyToMono(Document.class).
delaySubscription(Duration.ofSeconds(10)).
repeat5TimesWithDynamicTimeDelayUntil(!document.isEmpty()).
subscribe();

你好 贝尔纳多

是的,有可能。

Mono 有两个重新订阅的概念(因此,重新触发请求)

  • 重试 = 如果上游完成异常则重新订阅
  • 重复 = 如果上游成功完成则重新订阅

每个概念在 Mono 上都有多个针对不同用例的重载方法。寻找 retry*repeat* 方法。 例如,要无延迟重试最大次数,请使用 retry(int numRetries).

通过 retryWhenrepeatWhen 方法支持更复杂的用例,如以下示例所示。

重试时

如果单声道异常完成最多 5 次且每次尝试间隔 5 秒,则重试:

// From reactor-core >= v3.3.4.RELEASE
import reactor.util.retry.Retry;

this.webClient
        .post()
        .uri(SERVICE_URL)
        .body(BodyInserters.fromValue(docRequest))
        .retrieve()
        .bodyToMono(Document.class)
        .retryWhen(Retry.fixedDelay(5, Duration.ofSeconds(5)))
        .delaySubscription(Duration.ofSeconds(10))

重试构建器支持其他退避策略(例如指数)和其他选项以完全自定义重试。

请注意,上面使用的 retryWhen(Retry) 方法是在 reactor-core v3.3.4.RELEASE 中添加的,而 retryWhen(Function) 方法已弃用。 在 reactor-core v3.3.4.RELEASE 之前,您可以使用 reactor-extras 项目中的重试函数构建器来创建一个 Function 以传递给 retryWhen(Function).

重复

如果您需要重复成功,请使用 .repeatWhen.repeatWhenEmpty 而不是上面的 .retryWhen

使用 reactor-extras 项目中的重复函数生成器创建重复 Function,如下所示:

// From reactor-extras
import reactor.retry.Repeat;

this.webClient
        .post()
        .uri(SERVICE_URL)
        .body(BodyInserters.fromValue(docRequest))
        .retrieve()
        .bodyToMono(Document.class)
        .filter(document -> !document.isEmpty())
        .repeatWhenEmpty(Repeat.onlyIf(repeatContext -> true)
                .exponentialBackoff(Duration.ofSeconds(5), Duration.ofSeconds(10))
                .timeout(Duration.ofSeconds(30)))
        .delaySubscription(Duration.ofSeconds(10))

如果您想在成功或失败时重新订阅,您也可以将 .retry*.repeat* 链接起来。