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

Spring Reactive中Filter内retryWhen()不生效问题排查

问题:WebClient超时后自定义RetryFilter重试逻辑未执行

作为Spring Reactive新手,我用WebClient.Builder创建WebClient,其他功能正常,但请求超时后重试逻辑没触发。已经给构建器配置了自定义RetryFilter,调试能看到Filter被调用,但重试就是不执行,想知道是不是订阅模型的问题或者其他原因?

构建器配置代码

builder.filter(new RetryFilter())

RetryFilter代码

public class RetryFilter implements ExchangeFilterFunction {

    public RetryFilter() {
    }

    @Override
    public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
        return next.exchange(request)
                .flatMap(response ->
                        response.statusCode().is5xxServerError() ?
                                response.createException().flatMap(Mono::error) :
                                Mono.just(response)
                )
                .retryWhen(Retry.backoff(2, Duration.ofMillis(75)))
                .onErrorMap(e -> {
                    if (Exceptions.isRetryExhausted(e)) {
                        return e.getCause();
                    }
                    return e;
                });
    }
}

WebClient使用代码

return client.retrieve()
            .toEntity(String.class)
            .flatMap(response -> unmarshalResponse(response, input));

问题排查与解决方案

1. 超时异常未被纳入重试范围

你的RetryFilter当前只显式处理了5xx状态码转成的异常,但请求超时抛出的TimeoutException(或Netty的ReadTimeoutException)可能没被正确捕获重试。默认Retry.backoff会重试所有异常,但显式指定重试范围能避免意外情况:

.retryWhen(Retry.backoff(2, Duration.ofMillis(75))
        .retryOn(throwable -> 
                throwable instanceof ClientResponseException || 
                throwable instanceof TimeoutException ||
                throwable instanceof ReadTimeoutException))

2. 未正确配置WebClient超时

如果WebClient没有设置超时时间,请求不会触发超时异常,自然不会触发重试。需要给WebClient添加超时配置:

builder.clientConnector(new ReactorClientHttpConnector(HttpClient.create()
        .responseTimeout(Duration.ofSeconds(1)))); // 设置1秒响应超时

3. 检查错误信号传递逻辑

Filter中response.createException().flatMap(Mono::error)的写法是正确的,能将5xx响应转为ClientResponseException触发重试,但要注意:响应体被createException消耗后,重试时会重新发起请求,这是正常行为。

另外,onErrorMap中处理重试耗尽异常的逻辑不会影响重试触发,因为它是在重试逻辑执行完成后才生效的。

4. 验证重试触发状态

可以在retryWhen中添加日志,确认是否有异常进入重试流程:

.retryWhen(Retry.backoff(2, Duration.ofMillis(75))
        .doBeforeRetry(retrySignal -> 
                System.out.println("触发重试,异常原因:" + retrySignal.failure().getMessage())))

如果没有输出日志,说明异常没有到达retryWhen阶段,需要排查是否有其他Filter或操作提前消耗了错误信号。

5. 确认订阅环境的正确性

确保你的WebClient调用是在Reactive环境中被订阅的(比如Spring WebFlux控制器返回Mono/Flux),如果是在非Reactive环境中调用block(),也不影响重试逻辑,但要避免在流程中手动取消订阅。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 21:37:50