如果所有重试都用尽,Spring Webclient 重试并执行代码

Posted

技术标签:

【中文标题】如果所有重试都用尽,Spring Webclient 重试并执行代码【英文标题】:Spring Webclient retry and execute a code if all retries are exhausted 【发布时间】:2020-07-17 17:20:13 【问题描述】:

我有一个将事件发送到不同来源 (URL) 的 webhook 服务。按照设计,请求超时为 10s,如果失败,则重试发送 3 次。如果所有重试均失败,则必须执行代码以禁用 DB 中的该 URL。

到目前为止,我设法重试并延迟了 5 秒。但是,我不确定如何在失败后执行代码。

    try

          String body = objectMapper.writeValueAsString(webhookDTO);

                webClient.post()
                        .uri(webhook.getUrl())
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(body)
                        .exchange()
                        .timeout(Duration.ofSeconds(5))
                        .retryWhen(Retry.backoff(3, Duration.ofSeconds(5))
                                .jitter(0d)
                                .doAfterRetry(retrySignal -> 
                                    logger.info("Retried " + retrySignal.totalRetries());
                                )
                                .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) 
                                                    -> new WebhookTimeoutException()))
                        .doOnSuccess(clientResponse -> 
                            logger.info("Event is received by " + client);
                        )
                        .subscribe();
             catch (Exception e) 
                logger.error("Error on webhook dispatcher: ", e);
            

谁能举例说明如何做到这一点?

【问题讨论】:

在使用 Reactor 时请尽量避免使用 try-catch。 @PrashantPandey 感谢您的建议。你为什么这么认为? 【参考方案1】:

你快到了!只需使用doOnError,如下所示。这里的想法是,一旦所有尝试都失败了,你就抛出WebhookTimeoutException。仅当抛出错误并更新数据库时才调用 doOnError。异常类是可选的。你可以忽略它。

webClient.post()
        .uri(webhook.getUrl())
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(body)
        .exchange()
        .timeout(Duration.ofSeconds(5))
        .retryWhen(Retry.backoff(3, Duration.ofSeconds(5))
                .jitter(0d)
                .doAfterRetry(retrySignal -> 
                    logger.info("Retried " + retrySignal.totalRetries());
                )
                .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) 
                                    -> new WebhookTimeoutException()))
        .doOnSuccess(clientResponse -> 
            logger.info("Event is received by " + client);
        )
        .doOnError(WebhookTimeoutException.class, (msg) -> 
            System.out.println("Message :: " + msg);
            // here update the DB
            dbRepository.save(...);
        )        
        .subscribe();

【讨论】:

哇。有效。干杯。我试过doOnError,但在retryWhen()之前。我不知道有什么区别:) 注意这里使用了RetrySpec,在 reactor-core 3.3.4.RELEASE 中新引入(它允许使用特定的异常类来重试耗尽的情况,甚至可能是自定义的)

以上是关于如果所有重试都用尽,Spring Webclient 重试并执行代码的主要内容,如果未能解决你的问题,请参考以下文章

服务幂等设计与实践

重试与延迟调度系统-场景与设计

利用feign的重试机制刷新过期的请求Token

python的urllib3库(http连接池)

Spring Cloud Feign 重试机制-如何实现请求重试

自己动手实践 spring retry 重试框架