嵌套WebClient调用遇阻塞问题:.get()无限等待无响应
问题原因
你遇到的无限等待本质是阻塞操作占用了Reactor事件循环线程,导致整个反应式任务链无法被调度执行。WebFlux基于Reactor的事件循环模型,事件循环线程是异步任务的调度核心,如果在这个线程上调用get()这类阻塞方法,会把线程卡死,后续的getToken()、customWebClient.delete()这些异步任务根本没机会执行;直到超时抛出异常后,线程被释放,任务才开始调度执行。
解决方案
核心思路是避免在事件循环线程上执行阻塞操作,要么全程用反应式编程不阻塞,要么把阻塞操作放到专门的弹性线程池中执行。
方案1:用block()替代toFuture().get(),并切换线程池
直接使用Reactor提供的block()方法获取结果,同时通过subscribeOn切换到弹性线程池,避免阻塞事件循环:
import reactor.core.scheduler.Schedulers; import org.springframework.http.MediaType; import reactor.core.publisher.Mono; import java.util.Map; import org.springframework.core.ParameterizedTypeReference; // 切换到弹性线程池执行阻塞获取 Map<String, Object> result = someservice.getToken() .flatMap(token -> customWebclient.delete() .uri(uriBuilder -> uriBuilder.path("/users") .queryParam("id", id) .build()) .headers(headers -> headers.setBearerAuth(token)) .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {}) .log()) .subscribeOn(Schedulers.boundedElastic()) .block();
方案2:保留Future方式,但切换线程池
如果必须用Future,同样需要先切换线程池再转Future:
import reactor.core.scheduler.Schedulers; import java.util.concurrent.Future; import java.util.Map; Future<Map<String, Object>> future = someservice.getToken() .flatMap(token -> customWebclient.delete() .uri(uriBuilder -> uriBuilder.path("/users") .queryParam("id", id) .build()) .headers(headers -> headers.setBearerAuth(token)) .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {}) .log()) .subscribeOn(Schedulers.boundedElastic()) .toFuture(); Map<String, Object> result = future.get();
方案3:全程使用反应式编程(推荐)
如果业务场景允许,尽量不要阻塞,直接返回Mono<Map<String, Object>>,让WebFlux的异步模型处理整个流程,从根本上避免阻塞问题:
// 直接返回Mono,不做阻塞操作 public Mono<Map<String, Object>> deleteUser(String id) { return someservice.getToken() .flatMap(token -> customWebclient.delete() .uri(uriBuilder -> uriBuilder.path("/users") .queryParam("id", id) .build()) .headers(headers -> headers.setBearerAuth(token)) .header("Content-Type", MediaType.APPLICATION_JSON_VALUE) .header("Accept", MediaType.APPLICATION_JSON_VALUE) .retrieve() .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {}) .log()); }
补充说明
之前GET方法能正常工作是巧合,本质上同样存在阻塞事件循环的风险,只是某些情况下线程调度刚好没被卡死。WebFlux的设计初衷是异步非阻塞,尽量避免在反应式流程中插入阻塞操作,这是保证性能和稳定性的关键。
内容的提问来源于stack exchange,提问作者Sruthi
相关产品推荐
相关产品推荐

