繁体   English   中英

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

[英]Conditional repeat or retry on Mono with webclient from Spring WebFlux

我想要做的是使用 webclient 对 Webflux 中的 Mono 进行有条件的重复。情况如下:

我们有一些返回生成文档的业务休息服务服务。 此文档的生成是从在此之前调用的另一个服务触发的。 但是,回到正题:文档生成服务需要 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)方法已被弃用。 此前反应堆芯v3.3.4.RELEASE,你可以使用来自重试功能建造反应堆的额外项目,以创建一个Function传递给retryWhen(Function)

重复时间

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

使用reactor-extras项目中的repeat 函数构建来创建repeat 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*

添加重试

Mono 和 Flux API 中内置了两个关键的重试运算符。

使用重试

让我们使用 retry 方法,它可以防止应用程序立即返回错误并重新订阅指定次数:

public Mono<String> getData(String stockId) {
    return webClient.get()
        .uri(PATH_BY_ID, stockId)
        .retrieve()
        .bodyToMono(String.class)
        .retry(3);
}

这将最多重试 3 次,无论 Web 客户端返回什么错误。

使用 retryWhen

接下来,让我们尝试使用 retryWhen 方法的可配置策略:

public Mono<String> getData(String stockId) {
    return webClient.get()
        .uri(PATH_BY_ID, stockId)
        .retrieve()
        .bodyToMono(String.class)
        .retryWhen(Retry.max(3));
}

在这里,我们使用了最大策略来重试最大次数。 这相当于我们的第一个示例,但允许我们有更多的配置选项。

添加延迟

没有任何延迟地重试的主要缺点是这不会给失败的服务时间恢复。 它可能会压倒它,使问题变得更糟并降低恢复的机会。

使用 fixedDelay 重试

我们可以使用 fixedDelay 策略在每次尝试之间添加延迟:

public Mono<String> getData(String stockId) {
    return webClient.get()
      .uri(PATH_BY_ID, stockId)
      .retrieve()
      .bodyToMono(String.class)
      .retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(2)));
}

此配置允许尝试之间有两秒的延迟,这可能会增加成功的机会。

使用退避重试

我们可以使用退避策略,而不是在固定的时间间隔重试:

public Mono<String> getData(String stockId) {
    return webClient.get()
      .uri(PATH_BY_ID, stockId)
      .retrieve()
      .bodyToMono(String.class)
      .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)));
}

实际上,这在尝试之间增加了逐渐增加的延迟——在我们的示例中大致为 2、4 和 8 秒的间隔。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM