Spring Boot跨版本微服务异步调用异常:请求第三方服务时出现The underlying HTTP client completed without emitting a response错误
看起来你踩了跨版本Spring Boot微服务异步调用的坑!我帮你梳理几个常见的排查方向和解决方案,都是实际项目里踩过的经验:
一、先排查异步上下文传递的问题
Spring Boot 3.3.x(基于Spring Framework 6.1)和3.1.x(Spring Framework 6.0)在Reactor上下文传递的细节上有差异。当第二个微服务用异步方式调用第三方时,WebClient依赖的请求上下文(比如你用到的idSession头,或者认证信息)可能没被正确传递,导致请求发出去后,因为上下文缺失被静默终止,就会出现这个错误。
解决思路:
- 如果用
@Async注解实现异步,别依赖上下文自动传递请求头,在异步方法里手动把需要的头信息传给WebClient,比如显式调用.header("idSession", 传入的idSession值); - 如果用Reactor的响应式异步(比如
subscribeOn/publishOn),可以用Context手动传递上下文数据,或者把WebClient的请求逻辑和上下文绑定,比如:
Mono.just(idSession) .flatMap(sessionId -> webClient.post() .uri("/third-service/api") .header("idSession", sessionId) .retrieve() .bodyToMono(YourResponse.class)) .subscribeOn(Schedulers.boundedElastic()) .subscribe();
二、检查WebClient的配置差异
Spring Boot 3.3.3默认的WebClient底层客户端(Reactor Netty)版本比3.1.6高不少,新版本对超时、连接池的处理逻辑有变化。如果第二个微服务的WebClient没显式配置超时,可能请求因为超时被客户端主动终止,就会抛出这个模糊的错误。
解决思路:
给第二个微服务的WebClient加上明确的超时和连接配置,比如:
WebClient webClient = WebClient.builder() .clientConnector(new ReactorClientHttpConnector( HttpClient.create() .responseTimeout(Duration.ofSeconds(30)) // 响应超时 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) // 连接超时 .poolResources(PoolResources.fixed("third-service-pool", 10)) // 显式配置连接池 )) .build();
另外,检查是否开启了连接池的优雅关闭,跨版本的默认配置可能有冲突,显式配置更稳妥。
三、排查异步线程的生命周期问题
如果第二个微服务的异步调用是在主请求线程结束后才执行的,请求上下文已经被销毁,WebClient的请求可能还没完成就被中断了。比如用@Async但没正确配置线程池,或者异步任务没被正确等待。
解决思路:
- 如果用
@Async,确保异步方法返回CompletableFuture或者响应式类型(Mono/Flux),并且调用方要等待任务完成(比如用future.join(),在WebFlux场景下推荐用flatMap串联异步逻辑,不要直接subscribe); - 检查线程池配置,确保异步线程不会被过早回收,比如在
application.properties里配置:
spring.task.execution.pool.core-size=10 spring.task.execution.pool.max-size=20 spring.task.execution.pool.keep-alive=60s
四、开启详细日志定位问题
这个错误的提示太模糊,最好通过日志看请求的完整链路。在第二个微服务的配置文件里开启Reactor Netty和WebClient的DEBUG日志:
logging.level.reactor.netty.http.client=DEBUG logging.level.org.springframework.web.reactive.function.client=DEBUG
这样能看到请求是否成功建立连接、是否发送了请求体、有没有收到响应头,甚至能看到底层的TCP交互细节,帮你快速定位是请求没发出去,还是第三方服务没响应,或者客户端提前关闭了连接。
五、考虑JDK版本差异的影响
JDK 18到JDK22之间有一些HTTP客户端底层的变化,虽然Spring Boot做了封装,但也可能存在兼容问题。可以尝试让第二个微服务的WebClient强制使用Reactor Netty,而不是JDK自带的HttpClient,也就是上面提到的显式配置ReactorClientHttpConnector,避免依赖JDK原生的客户端实现。
备注:内容来源于stack exchange,提问作者Serg3nt3




