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

如何在Spring WebClient中捕获超时异常并调用指定方法?

解决方案:捕获WebClient超时异常并调用错误计数方法

你遇到的核心问题是:WebClient的响应式操作中,超时异常是在Reactive流内部异步抛出的,外层同步的try/catch无法捕获。要实现超时触发strategy.incrErrorCount(port)的需求,需要利用Reactive流的错误处理操作符来处理。

具体修改方案

通过doOnError操作符可以在异常发生时执行副作用逻辑(比如错误计数),同时不改变流的错误状态。调整后的代码如下:

private Mono<GameEntity> callApplication(GameEntity gameEntity) throws URISyntaxException {
    // 建议将WebClient实例化移到方法外作为类成员,避免重复创建实例
    WebClient client = WebClient.create();
    for(int i=0;i<NUM_OF_RETRY;++i) {
        String port = strategy.getNextInstancePort();
        URI uri = new URI(String.format("http://localhost:%s/game/add", port));
        
        return client.post()
                .uri(uri)
                .contentType(MediaType.APPLICATION_JSON)
                .body(Mono.just(gameEntity), GameEntity.class)
                .retrieve()
                .bodyToMono(GameEntity.class)
                .timeout(Duration.ofSeconds(3))
                // 精准捕获超时异常,执行错误计数
                .doOnError(TimeoutException.class, e -> {
                    strategy.incrErrorCount(port);
                })
                // 捕获WebClient请求异常,按需处理
                .doOnError(WebClientRequestException.class, e -> {
                    // 可添加请求异常的自定义处理逻辑
                })
                // 捕获其他未匹配的异常,执行错误计数
                .doOnError(Exception.class, e -> {
                    strategy.incrErrorCount(port);
                });
    }
    // 重试耗尽后返回空Mono,符合Reactive规范(避免返回null)
    return Mono.empty();
}

关键说明

  • doOnError的作用:专门用于在流触发错误时执行副作用操作,不会中断或改变流的错误传播,异常会继续向下传递供上层处理。
  • 精准异常匹配:通过指定TimeoutException.class,可以只在超时场景下触发错误计数,避免误处理其他类型的异常。
  • Reactive规范优化:将原有的return null改为return Mono.empty(),符合响应式流的设计原则,避免后续操作出现空指针问题。
  • WebClient复用:把WebClient.create()移到方法外部作为类成员变量,减少重复创建实例的性能开销。

如果想实现更优雅的响应式重试(替代原有的同步for循环),可以结合retryWhen操作符:

private Mono<GameEntity> callApplication(GameEntity gameEntity) {
    WebClient client = WebClient.create();
    return Mono.defer(() -> {
        String port = strategy.getNextInstancePort();
        URI uri;
        try {
            uri = new URI(String.format("http://localhost:%s/game/add", port));
        } catch (URISyntaxException e) {
            return Mono.error(e);
        }
        return client.post()
                .uri(uri)
                .contentType(MediaType.APPLICATION_JSON)
                .body(Mono.just(gameEntity), GameEntity.class)
                .retrieve()
                .bodyToMono(GameEntity.class)
                .timeout(Duration.ofSeconds(3))
                .doOnError(TimeoutException.class, e -> strategy.incrErrorCount(port))
                .doOnError(WebClientRequestException.class, e -> strategy.incrErrorCount(port));
    })
    .retryWhen(Retry.fixedDelay(NUM_OF_RETRY, Duration.ofMillis(100)));
}

这个版本用Mono.defer动态获取每次重试的目标端口,结合retryWhen实现响应式重试逻辑,更贴合Reactive编程的风格。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:33:10