Spring WebClient能否配置多retryWhen处理不同错误响应?
如何为Spring WebClient添加多场景的差异化重试策略
你遇到的问题很典型:连续调用retryWhen()确实会覆盖之前的重试配置,因为每个retryWhen操作会替换上游的重试逻辑,而不是叠加多个规则。要实现针对不同错误场景的差异化重试,我们需要把多个策略合并成一个统一的重试规格,或者通过组合方式实现。
先修正你的异常判断逻辑
首先注意你原来的is5xx只判断了503(ServiceUnavailable),但需求是覆盖502和503,所以先调整Predicate:
protected static final Predicate<Throwable> is401 = throwable -> throwable instanceof WebClientResponseException.Unauthorized; protected static final Predicate<Throwable> is5xxServerError = throwable -> throwable instanceof WebClientResponseException.ServiceUnavailable || // 503 (throwable instanceof WebClientResponseException && ((WebClientResponseException) throwable).getStatusCode() == HttpStatus.BAD_GATEWAY); // 502 protected static final Predicate<Throwable> is429 = throwable -> throwable instanceof WebClientResponseException.TooManyRequests;
方案一:构建单一的定制化Retry规格
这种方式适合所有场景共享最大重试次数,且只需要区分延迟和前置操作的情况:
// 构建定制化重试规则 Retry multiScenarioRetry = Retry.custom() .maxAttempts(5) // 所有场景最多重试5次,可按需调整 // 只对我们关心的异常触发重试 .filter(throwable -> is401.test(throwable) || is5xxServerError.test(throwable) || is429.test(throwable)) // 根据异常类型返回不同的重试延迟 .backoff(context -> { Throwable failure = context.failure(); if (is401.test(failure)) { return Duration.ofSeconds(0); // 401时立即重试 } else if (is5xxServerError.test(failure)) { return Duration.ofSeconds(5); // 502/503延迟5秒重试 } else if (is429.test(failure)) { return Duration.ofSeconds(20); // 429延迟20秒重试 } return Duration.ofSeconds(0); // 默认 fallback,不会触发 }) // 401重试前先执行令牌刷新逻辑 .doBeforeRetry(retrySignal -> { if (is401.test(retrySignal.failure())) { // 这里替换成你的令牌刷新逻辑 // refreshAccessToken(); System.out.println("Refreshing access token before retrying 401..."); } }) // 重试耗尽后抛出原异常 .onRetryExhaustedThrow((spec, signal) -> signal.failure());
然后将这个统一的Retry规格应用到WebClient:
WebClient.create() .get() .uri("endpointuri") .retrieve() .bodyToFlux(String.class) .retryWhen(multiScenarioRetry);
方案二:组合多个独立的Retry实例
如果每个错误场景需要不同的最大重试次数(比如401只重试2次,5xx重试5次),可以用Retry.from()组合多个独立的Retry规则:
// 分别定义每个场景的重试规则 Retry retry401 = Retry.fixedDelay(2, Duration.ofSeconds(0)) .filter(is401) .doBeforeRetry(signal -> { // 401重试前刷新令牌 // refreshAccessToken(); }) .onRetryExhaustedThrow((spec, signal) -> signal.failure()); Retry retry5xx = Retry.fixedDelay(5, Duration.ofSeconds(5)) .filter(is5xxServerError) .onRetryExhaustedThrow((spec, signal) -> signal.failure()); Retry retry429 = Retry.fixedDelay(3, Duration.ofSeconds(20)) .filter(is429) .onRetryExhaustedThrow((spec, signal) -> signal.failure()); // 组合多个重试规则,匹配到任意一个就执行对应重试 Retry combinedRetry = Retry.from(context -> Flux.first( retry401.generateRetrySignals(context), retry5xx.generateRetrySignals(context), retry429.generateRetrySignals(context) ) );
同样将combinedRetry应用到WebClient的retryWhen即可。
关键说明
- 避免连续调用
retryWhen():每个retryWhen会替换上游的重试逻辑,所以最后一个会覆盖之前所有的配置。 - 401场景的令牌刷新:必须在重试前完成,否则重试依然会失败,这里用
doBeforeRetry来执行前置操作是最合适的时机。 - 异常过滤:通过
filter确保只有目标异常才会触发重试,避免无关异常被重试。
内容的提问来源于stack exchange,提问作者Naveen Kumar
相关产品推荐
相关产品推荐

