java 使用Spring WebFlux的Web客户端在Mono上进行条件性重复或重试

sxpgvts3  于 2023-02-02  发布在  Java
关注(0)|答案(1)|浏览(233)

我想在WebFlux中使用WebClientMono上实现一个条件重复。情况如下:
我们有一个rest服务,它返回一个生成的文档。这个文档的生成是由在它之前调用的另一个服务触发的。文档生成服务需要10-30秒。
我们希望在10秒后检查是否生成了文档(Mono<Document>)。如果是,则一切正常。如果不是,则在另一个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();
brgchamk

brgchamk1#

是的,有可能。
Mono有两个重新订阅(从而重新触发请求)的概念

    • retry *=如果上游完成但出现异常,则重新订阅
    • repeat *=如果上游成功完成,则重新订阅

每个概念在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项目中的retry函数构建器创建一个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*链接在一起。

相关问题