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

Spring WebClient超时后无法重试问题求助(附代码)

问题解决:WebClient超时后无法触发重试的修复方案

问题根源

你的代码存在两个核心问题:

  1. 操作符顺序错误:timeout被放在了retryWhen之后。Reactor链式操作按顺序执行,retryWhen只能捕获它上游流产生的异常,下游的timeout抛出的异常会直接终止整个流,无法进入重试逻辑。
  2. 异常判断逻辑不准确:timeout操作符直接抛出的是reactor.core.Exceptions.TimeoutException,并非作为其他异常的cause存在,之前的过滤条件无法匹配到该异常。

修复步骤

  1. 调整操作符顺序:将timeout移到retryWhen之前,让超时异常能被重试策略捕获。
  2. 修正重试过滤条件:直接判断异常是否为Reactor的TimeoutException。

修改后的完整代码

@Service
public class RestV2ServiceImpl implements RestV2Service {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestV2ServiceImpl.class);

    @Value("${apirest.server.requestTimeoutInSeconds}")
    private Long requestTimeout;

    @Value("${retryPolicy.maxAttempts}")
    private Integer restV2retryPolicyMaxAttempts;

    @Value("${retryPolicy.periodInMilliseconds}")
    private Long restV2initialInterval;

    @Autowired
    private WebClient webClient;

    @Override
    public String updateRequestV2(String url, String requestJson, String versionSequence) {
        return webClient.patch()
                .uri(url)
                .header(HttpHeaders.CONTENT_TYPE, "application/json-patch+json")
                .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                .header("If-Match", versionSequence)
                .body(BodyInserters.fromValue(requestJson))
                .retrieve()
                .onStatus(this::retryableStatuses, this::retryableStatusException)
                .bodyToMono(String.class)
                // 核心调整:将timeout移至retryWhen之前
                .timeout(Duration.ofSeconds(requestTimeout))
                .retryWhen(retryPolicy())
                .doOnError(throwable -> LOGGER.error(throwable.getMessage()))
                .block();
    }

    private boolean retryableStatuses(HttpStatus status) {
        return status.is5xxServerError();
    }

    private Mono<? extends Throwable> retryableStatusException(ClientResponse response) {
        return response.bodyToMono(String.class).map(body ->
                new HttpServerErrorException(response.statusCode(), body));
    }

    private Retry retryPolicy() {
        return Retry.backoff(restV2retryPolicyMaxAttempts, Duration.ofMillis(restV2initialInterval))
                // 修正TimeoutException的匹配逻辑
                .filter(throwable -> throwable instanceof HttpServerErrorException 
                        || throwable instanceof WebClientRequestException 
                        || throwable instanceof reactor.core.Exceptions.TimeoutException)
                .onRetryExhaustedThrow(((retryBackoffSpec, retrySignal) -> 
                        new RetryExhaustedException(retrySignal.failure().getMessage())));
    }
}

关键说明

  • 操作符顺序逻辑:Reactor流的每个操作符仅处理上游传递的信号,retryWhen必须放在所有可能产生重试异常的操作之后,这样才能捕获到这些异常并触发重试。
  • 异常类型匹配:确保导入的是reactor.core.Exceptions.TimeoutException,而非java.util.concurrent.TimeoutException(两者虽有继承关系,但直接判断更精准)。
  • 重试边界处理:当重试次数耗尽时,onRetryExhaustedThrow会抛出RetryExhaustedException,可根据业务需求调整最终的异常兜底逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 11:47:15