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

Spring Boot WebClient仅遇5xx错误重试失效问题排查求助

问题排查与解决方案

核心问题分析

你的代码无法触发重试的关键原因可能有两个:

  1. 自定义异常的包装问题:如果ServiceException继承自Exception(而非RuntimeException),WebFlux会将其包装在RuntimeException中抛出,导致filter(ServiceException.class::isInstance)无法匹配到目标异常,重试逻辑直接失效。
  2. 异常捕获范围偏差:若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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:30:46