WebClient未发起HTTP请求及无法等待请求完成的问题排查
问题根源
你碰到的核心问题是Reactor的惰性执行特性——所有响应式流操作(包括你发起的HTTP请求)只有在被订阅的时候才会真正触发执行。你当前的代码里,Flux.fromIterable(monoList).flatMap(...).then()只是构建了一个描述"要执行什么逻辑"的Mono对象,但并没有真正启动这些请求;而同步的日志打印Finished waiting会直接先执行,之后订阅动作才会触发异步的HTTP请求,这就导致了日志顺序完全颠倒,看起来像是代码没等待请求完成。
解决方法
根据你的运行环境分两种场景处理:
场景1:非响应式环境(比如定时任务、Spring MVC服务)
如果你的代码运行在非响应式环境中(比如用@Scheduled注解的定时任务),可以用block()来阻塞当前线程,直到所有请求全部完成:
log.info("Start waiting for requests"); Mono<Void> allRequestsDone = Flux.fromIterable(monoList) .flatMap(Function.identity()) // 并行执行所有Mono请求 .then(); allRequestsDone.block(); // 阻塞当前线程,直到所有请求完成 log.info("Finished waiting for all requests!");
注意:
block()会阻塞线程,绝对不要在WebFlux这类响应式框架的控制器/事件循环线程中使用,会破坏响应式模型的非阻塞特性。
场景2:响应式环境(比如WebFlux控制器、Reactive服务)
如果是在响应式环境中,应该让Reactor框架来处理订阅逻辑,不要手动阻塞,而是通过操作符绑定完成后的逻辑:
log.info("Starting all requests"); Mono<Void> allRequestsDone = Flux.fromIterable(monoList) .flatMap(Function.identity(), 10) // 第二个参数可控制并行度,避免同时发起过多请求压垮目标服务 .doOnComplete(() -> log.info("Finished waiting for all requests!")) .then(); // 将Mono返回给框架,由框架负责订阅触发执行 return allRequestsDone;
你之前用subscribe()是异步订阅,不会阻塞当前线程,所以主线程会直接继续执行打印日志,这也是日志顺序混乱的直接原因。
额外优化建议
- 异常处理优化:你在
map里用try-catch处理JSON解析异常,建议改用Reactor的onError*操作符统一处理,比如onErrorReturn(null)或onErrorMap()转换异常,避免在map里处理异常破坏响应式流的语义。 - WebClient配置优化:当前WebClient没有配置baseUrl,建议构建时设置
baseUrl("http://localhost:8080"),这样uri只需要写"/api/some/url"更简洁。 - 并行度控制:
flatMap默认并行度是256,如果你的队列很大,手动设置合适的并行度可以避免目标服务被请求压垮。
内容的提问来源于stack exchange,提问作者gstackoverflow
相关产品推荐
相关产品推荐

