Spring WebClient实现非阻塞异步HTTP调用及超时配置
Spring WebClient 非阻塞异步HTTP调用实现方案
你现有代码仅完成了响应式流的组装,未触发实际请求,也缺少非阻塞回调、单请求超时配置,针对两个问题的实现方式如下:
非阻塞读取解析响应(不阻塞业务线程)
Reactor响应式流是懒加载的,只有显式订阅才会真正发起HTTP请求,禁止调用block()、get()这类阻塞方法,否则会让当前业务线程同步等待响应,完全失去异步非阻塞的特性。
正确做法是调用Mono.subscribe()方法注册回调函数:业务线程执行到订阅逻辑时会立刻返回,不会等待HTTP响应;等服务端响应返回、内容解析完成后,会由WebClient绑定的异步线程池调度执行回调逻辑处理结果,请求过程中业务线程不会被占用。
核心代码逻辑:
responseMono.subscribe( // 响应正常返回、解析完成后异步执行 responseJson -> { // 此处编写你的响应处理、后续业务逻辑 System.out.println("收到异步HTTP响应:" + responseJson); }, // 请求失败、解析失败、超时等异常场景异步执行 error -> { // 此处编写异常日志、降级、告警逻辑 log.error("异步HTTP调用异常", error); } );
为单请求设置独立超时时间
不要修改WebClient全局超时配置,直接在当前请求的响应式流上添加配置即可,有两种常用方式:
- 快速配置总超时:通过
timeout()算子直接设置当前请求的最大等待时长,超时后会自动抛出TimeoutException,被上述异常回调捕获,同时自动释放请求连接:Mono<JsonObject> responseMono = headersSpec.retrieve() .bodyToMono(JsonObject.class) // 示例设置当前请求总超时为5秒,可按需传入Duration调整时长 .timeout(Duration.ofSeconds(5)); - 细粒度配置不同阶段超时:如果需要单独设置连接超时、读取超时,可在请求构建阶段传入对应属性:
Mono<JsonObject> responseMono = headersSpec .httpRequest(request -> { ClientHttpRequest clientReq = request.getClientRequest(); // 单独设置连接超时2秒 clientReq.addAttribute(ClientHttpRequest.CONNECT_TIMEOUT_ATTR, Duration.ofSeconds(2).toMillis()); // 单独设置读取响应超时5秒 clientReq.addAttribute(ClientHttpRequest.READ_TIMEOUT_ATTR, Duration.ofSeconds(5).toMillis()); }) .retrieve() .bodyToMono(JsonObject.class);
完整改造后可用代码
import java.time.Duration; import java.util.Map; // 其余依赖按需引入 public void asyncHttp(HTTPMethod method, String url, String body, Map<String, String> headers){ WebClient.UriSpec<WebClient.RequestBodySpec> uriSpec = webClient.method(method.getMethod()); WebClient.RequestBodySpec bodySpec = uriSpec.uri(url); WebClient.RequestHeadersSpec<?> headersSpec = bodySpec.bodyValue(body); for(Map.Entry<String, String> entry : headers.entrySet()){ headersSpec.header(entry.getKey(), entry.getValue()); } Mono<JsonObject> responseMono = headersSpec.retrieve() .bodyToMono(JsonObject.class) // 配置当前请求独立超时 .timeout(Duration.ofSeconds(5)); // 非阻塞订阅,业务线程执行到此处立刻返回 responseMono.subscribe( resp -> { // 自定义响应处理逻辑 log.info("异步请求响应:{}", resp); }, err -> { // 自定义异常处理逻辑 log.error("异步请求失败", err); } ); }
补充说明:如果需要做链路追踪等上下文透传,可以在订阅时通过
subscriberContext传入上下文参数,或指定自定义调度器适配线程上下文传递逻辑。
内容的提问来源于stack exchange,提问作者Anuja Barve
相关产品推荐
相关产品推荐

