Spring WebClient超时后无法重试问题求助(附代码)
问题解决:WebClient超时后无法触发重试的修复方案
问题根源
你的代码存在两个核心问题:
- 操作符顺序错误:
timeout被放在了retryWhen之后。Reactor链式操作按顺序执行,retryWhen只能捕获它上游流产生的异常,下游的timeout抛出的异常会直接终止整个流,无法进入重试逻辑。 - 异常判断逻辑不准确:
timeout操作符直接抛出的是reactor.core.Exceptions.TimeoutException,并非作为其他异常的cause存在,之前的过滤条件无法匹配到该异常。
修复步骤
- 调整操作符顺序:将
timeout移到retryWhen之前,让超时异常能被重试策略捕获。 - 修正重试过滤条件:直接判断异常是否为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
相关产品推荐
相关产品推荐

