[英]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)
。
通过retryWhen
和repeatWhen
方法支持更复杂的用例,如以下示例所示。
要在单声道以异常情况完成时重试最多 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 方法的可配置策略:
public Mono<String> getData(String stockId) {
return webClient.get()
.uri(PATH_BY_ID, stockId)
.retrieve()
.bodyToMono(String.class)
.retryWhen(Retry.max(3));
}
在这里,我们使用了最大策略来重试最大次数。 这相当于我们的第一个示例,但允许我们有更多的配置选项。
没有任何延迟地重试的主要缺点是这不会给失败的服务时间恢复。 它可能会压倒它,使问题变得更糟并降低恢复的机会。
我们可以使用 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.