首页 > 解决方案 > Spring WebClient 多次重试时处理不同的错误

问题描述

我可以添加多个retryWhen来执行重试以处理不同的 WebClient 故障响应吗?

我想要达到的目标:

我正在使用 WebClient 进行 REST API 调用。很少有错误场景,发生时我需要执行重试,但延迟不同。

例如,1.如果401 Unauthorize发生,我可以在刷新令牌后立即重试。2. 如果502/503 Server发生错误,我需要延迟 5 秒后重试。3.如果429 Too Many Request发生,我需要延迟重试时间,比如20秒后。

我想创建如下重试规范:

    protected static final Predicate<Throwable> is401 =
                (throwable) -> throwable instanceof WebClientResponseException.Unauthorized;

        protected static final Predicate<Throwable> is5xx =
                (throwable) -> throwable instanceof WebClientResponseException.ServiceUnavailable;

        protected static final Predicate<Throwable> is429 =
                (throwable) -> throwable instanceof WebClientResponseException.TooManyRequests;

        Retry retry401 = Retry.fixedDelay(5, Duration.ofSeconds(1))
                    .filter(is401)
                    .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> retrySignal.failure());

        Retry retry5xx = Retry.fixedDelay(5, Duration.ofSeconds(10))
                    .filter(is5xx)
                    .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> retrySignal.failure());

        Retry retry429 = Retry.fixedDelay(5, Duration.ofSeconds(20))
                    .filter(is429)
                    .onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> retrySignal.failure());

// trying to apply the to WebClient like below:
    WebClient.Builder()
        .get()
        .uri("endpointuri")
        .retrieve()
        .bodyToFlux(String.class)
        .retryWhen(retry401)
        .retryWhen(retry5xx)
        .retryWhen(retry429);

看起来 `.retryWhen(retry429)' 会覆盖其他重试。

标签: spring-bootproject-reactorspring-webclientspring-reactive

解决方案


看起来 `.retryWhen(retry429)' 会覆盖其他重试。

这是错误的。retryWhen是一个基于现有发布者的复合运算符 - 您可以将其链接多次。您唯一需要担心一次重试“覆盖”另一次的情况是您的filter谓词重叠时。

即使在这种情况下,看起来它是“第一次获胜”(在链中)而不是“最后一次获胜”。

相反,您的问题可能与此行有关:

protected static final Predicate<Throwable> is5xx =
            (throwable) -> throwable instanceof WebClientResponseException.ServiceUnavailable;

从您的命名和描述来看,您似乎希望它捕获任何 5xx 错误 - 但它只会捕获 503(专门分配给“服务不可用”。)

如果您尝试使用不同的东西,例如 502 或 500 错误 - 那么您定义的所有谓词(因此重试)都不会匹配。

相反,要检查任何 5xx 错误,您可能需要:

protected static final Predicate<Throwable> is5xx =
        (throwable) -> throwable instanceof WebClientResponseException && ((WebClientResponseException)throwable).getStatusCode().is5xxServerError();

推荐阅读