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

Spring WebClient:指定异常重试后返回兜底值失败问题

问题原因

你的代码里,Retry.max(3)在重试3次耗尽后,会把原始的IllegalStateException包装成RetryExhaustedException抛出。而你用onErrorReturn(IllegalStateException.class, ...)只能匹配直接抛出的IllegalStateException,无法识别被包装后的异常,所以兜底值不会返回。

解决方案

有两种常用的修复方式:

方式一:让重试耗尽时抛出原始异常

在Retry配置里添加onRetryExhaustedThrow,指定重试耗尽后直接抛出原始异常,这样onErrorReturn就能正确匹配:

public Mono<String> getEmployee(String request) {
    return WebClient.create()
            .get()
            .uri("/accounts/" + request)
            .exchangeToMono(clientResponse -> {
                // 优化状态码判断,直接用枚举比较更清晰
                if (clientResponse.statusCode().equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
                    return Mono.error(new IllegalStateException());
                }
                if (clientResponse.statusCode().equals(HttpStatus.OK)) {
                    return clientResponse.bodyToMono(String.class);
                }
                return Mono.error(new IllegalCallerException());
            })
            .retryWhen(Retry.max(3)
                    .filter(throwable -> throwable instanceof IllegalStateException)
                    // 关键配置:重试耗尽时抛出原始异常
                    .onRetryExhaustedThrow((spec, signal) -> signal.failure()))
            .onErrorReturn(IllegalStateException.class, "TECHNICAL_ERROR")
            .log();
}

方式二:匹配包装后的异常

如果不想修改Retry配置,可以让onErrorReturn判断异常链,匹配RetryExhaustedException且其原因是IllegalStateException:

public Mono<String> getEmployee(String request) {
    return WebClient.create()
            .get()
            .uri("/accounts/" + request)
            .exchangeToMono(clientResponse -> {
                if (clientResponse.statusCode().equals(HttpStatus.INTERNAL_SERVER_ERROR)) {
                    return Mono.error(new IllegalStateException());
                }
                if (clientResponse.statusCode().equals(HttpStatus.OK)) {
                    return clientResponse.bodyToMono(String.class);
                }
                return Mono.error(new IllegalCallerException());
            })
            .retryWhen(Retry.max(3).filter(throwable -> throwable instanceof IllegalStateException))
            // 匹配包装后的异常
            .onErrorReturn(throwable -> 
                throwable instanceof RetryExhaustedException 
                && throwable.getCause() instanceof IllegalStateException, 
                "TECHNICAL_ERROR")
            .log();
}
补充说明

第一种方式更直观,能保持异常类型的一致性;第二种方式不需要改动Retry逻辑,适合需要保留RetryExhaustedException上下文的场景。另外,建议在exchangeToMono里用Mono.error()抛出异常,而不是直接throw,因为Reactor中直接抛出 unchecked 异常虽然能被捕获,但用Mono.error()更符合响应式编程的规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 18:10:34