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
相关产品推荐
相关产品推荐

