Spring Boot WebClient仅遇5xx错误重试失效问题排查求助
问题排查与解决方案
核心问题分析
你的代码无法触发重试的关键原因可能有两个:
- 自定义异常的包装问题:如果
ServiceException继承自Exception(而非RuntimeException),WebFlux会将其包装在RuntimeException中抛出,导致filter(ServiceException.class::isInstance)无法匹配到目标异常,重试逻辑直接失效。 - 异常捕获范围偏差:若
onStatus抛出的异常未被正确传递到retryWhen阶段,也会导致重试不触发;另外4xx错误会抛出默认的WebClientResponseException,不会进入重试流程(这符合你的需求,但需确认是否需要额外显式处理)。
修复方案一:修正自定义异常与重试过滤
步骤1:调整ServiceException继承关系
确保它是RuntimeException的子类,避免被WebFlux包装:
public class ServiceException extends RuntimeException { private final int statusCode; public ServiceException(String message, int statusCode) { super(message); this.statusCode = statusCode; } public int getStatusCode() { return statusCode; } }
步骤2:加固重试过滤逻辑(可选)
如果仍存在异常包装情况,修改filter逻辑检查异常的根源:
.filter(throwable -> throwable instanceof ServiceException || (throwable instanceof RuntimeException && throwable.getCause() instanceof ServiceException) )
修复方案二:无需自定义异常,直接基于响应状态码重试
这种方式更简洁,避免自定义异常的潜在问题,直接在重试逻辑中判断状态码:
public Mono<String> call(String input) { log.info("Sending echo call for: {}", input); return webClient.get() .uri(String.format("/echo/%s", input)) .accept(MediaType.APPLICATION_JSON) .acceptCharset(StandardCharsets.UTF_8) .retrieve() // 4xx客户端错误直接抛出,不触发重试 .onStatus(HttpStatus::is4xxClientError, response -> Mono.error(new WebClientResponseException( response.rawStatusCode(), response.statusCode().getReasonPhrase(), response.headers().asHttpHeaders(), null, StandardCharsets.UTF_8, response.request() )) ) .bodyToMono(String.class) .timeout(Duration.ofMillis(60_000L)) .retryWhen(Retry.backoff(3, Duration.ofSeconds(4)) .jitter(0d) // 仅重试5xx服务器错误对应的异常 .filter(throwable -> { if (throwable instanceof WebClientResponseException) { WebClientResponseException ex = (WebClientResponseException) throwable; return HttpStatus.valueOf(ex.getStatusCode()).is5xxServerError(); } // 可按需添加其他需要重试的异常(如超时) return throwable instanceof TimeoutException; }) .doAfterRetry(retrySignal -> log.info("Retry call due to: {}", retrySignal.failure().getMessage()) ) .onRetryExhaustedThrow((spec, signal) -> signal.failure()) ) .doOnError(throwable -> log.error("Call failure: {}", throwable.getMessage())) .doOnSuccess(response -> log.info("Success: {}", response)); }
关键说明
- 4xx错误控制:通过
onStatus显式处理4xx错误,确保这类错误直接抛出不重试。 - 重试范围精准控制:在
retryWhen的filter中仅匹配5xx对应的WebClientResponseException,同时可按需扩展其他重试场景(如请求超时)。 - 重试参数配置:
backoff(3, Duration.ofSeconds(4))表示最多重试3次,每次间隔固定4秒,jitter(0d)关闭间隔抖动保证重试时间稳定。
内容的提问来源于stack exchange,提问作者Gagan
相关产品推荐
相关产品推荐

