【问题标题】:Conditional repeat or retry on Mono with webclient from Spring WebFlux使用 Spring WebFlux 的 webclient 在 Mono 上进行有条件的重复或重试
【发布时间】:2019-09-19 06:09:30
【问题描述】:

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

我们有一些返回生成文档的业务休息服务。此文档的生成是由在此之前调用的另一个服务触发的。但是,回到正题:文档生成服务需要 10-30 秒。我们要做的是:在 10 秒后检查是否生成了文档(单声道)。如果是这样,一切都很好。如果没有,请在 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();

问候 贝尔纳多

【问题讨论】:

    标签: java spring webclient spring-webflux project-reactor


    【解决方案1】:

    是的,有可能。

    Mono 有两个概念用于重新订阅(从而重新触发请求)

    • 重试 = 如果上游完成异常则重新订阅
    • 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 项目的重试函数构建器来创建一个 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* 链接起来。

    【讨论】:

    • 好吧,我试过了,直到一件事它工作正常。似乎 delaySubscription 在重复中再次执行。所以我等了 20 秒,直到退避进入游戏。可以吗?稍后将粘贴我的代码....
    • 编辑将 delaySubscription 移到重试/重复之后,以便在每次重试/重复时不会执行延迟
    • 好的,明天再试,会给出反馈。谢谢。
    • @wild_nothing 使用onErrorMap
    • @Matley retry* 仅在发生异常时适用。要在 Mono 完成为空时重新订阅,请使用 Mono.repeatWhenEmpty
    猜你喜欢
    • 2020-07-31
    • 2020-10-01
    • 2020-04-11
    • 2021-01-11
    • 2019-05-30
    • 2018-06-11
    • 2019-02-14
    • 2018-05-09
    • 2021-09-10
    相关资源
    最近更新 更多