You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebClient多重试规则互相重置问题及解决办法问询

解决WebClient多重试规则计数器重置问题

问题根源

原代码中两个独立的retryWhen操作各自维护重试计数器,当错误交替出现(如500→429→500→429)时,每个retryWhen的过滤器会跳过不匹配的异常,导致各自的计数器无法累计,理论上会触发无限重试。以下是两种需求对应的解决方案:


方案一:两类错误各最多重试3次(总上限6次)

通过自定义RetryContext分别跟踪429和其他错误的重试次数,在同一个retryWhen中实现差异化退避和计数控制:

public ExchangeFilterFunction filterFunction() {
    return (req, next) -> next.exchange(req)
            .flatMap(response -> Mono.just(response)
                    .filter(it -> it.statusCode().isError())
                    .flatMap(it -> it.createException())
                    .flatMap(Mono::error)
                    .thenReturn(response))
            .retryWhen(Retry.custom()
                    // 初始化上下文,存储两类错误的重试次数
                    .retryContextSupplier(context -> new HashMap<String, Integer>() {{
                        put("non429Retries", 0);
                        put("429Retries", 0);
                    }})
                    // 每次重试前更新对应错误的计数
                    .doBeforeRetry(retrySignal -> {
                        Map<String, Integer> retryCounts = (Map<String, Integer>) retrySignal.context().attribute("retryContext");
                        Throwable throwable = retrySignal.failure();
                        if (throwable instanceof WebClientResponseException.TooManyRequests) {
                            retryCounts.put("429Retries", retryCounts.get("429Retries") + 1);
                        } else {
                            retryCounts.put("non429Retries", retryCounts.get("non429Retries") + 1);
                        }
                    })
                    // 判断是否继续重试:对应类型错误未达到3次上限
                    .filter(retrySignal -> {
                        Map<String, Integer> retryCounts = (Map<String, Integer>) retrySignal.context().attribute("retryContext");
                        Throwable throwable = retrySignal.failure();
                        if (throwable instanceof WebClientResponseException.TooManyRequests) {
                            return retryCounts.get("429Retries") < 3;
                        } else {
                            return retryCounts.get("non429Retries") < 3;
                        }
                    })
                    // 根据异常类型返回对应退避策略
                    .backoff(retrySignal -> {
                        Throwable throwable = retrySignal.failure();
                        if (throwable instanceof WebClientResponseException.TooManyRequests) {
                            // 429使用指定退避时长(注意原代码1000秒可能是笔误,可按需调整)
                            return Backoff.fixed(Duration.ofSeconds(1000));
                        } else {
                            return Backoff.exponentialBackoff(Duration.ofMillis(100), 2);
                        }
                    })
                    .build());
}

方案二:所有错误合计最多重试3次

统一总重试次数为3次,根据异常类型切换退避策略,避免计数器重置问题:

public ExchangeFilterFunction filterFunction() {
    return (req, next) -> next.exchange(req)
            .flatMap(response -> Mono.just(response)
                    .filter(it -> it.statusCode().isError())
                    .flatMap(it -> it.createException())
                    .flatMap(Mono::error)
                    .thenReturn(response))
            .retryWhen(Retry.backoff(3, Duration.ofMillis(100))
                    // 针对429错误替换退避策略
                    .backoff(retrySignal -> {
                        Throwable throwable = retrySignal.failure();
                        if (throwable instanceof WebClientResponseException.TooManyRequests) {
                            return Backoff.fixed(Duration.ofSeconds(1000));
                        }
                        // 其他错误沿用默认指数退避
                        return retrySignal.backoff();
                    })
                    // 过滤所有需要重试的WebClient错误异常
                    .filter(th -> th instanceof WebClientResponseException &&
                            ((WebClientResponseException) th).getStatusCode().isError()));
}

核心思路

将两个独立的retryWhen合并为一个,通过同一上下文维护重试计数或统一总重试次数,避免不同重试规则的计数器互相重置,从根源上解决无限重试风险。

内容的提问来源于stack exchange,提问作者Whiteyesx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 13:53:22