本文讲解如何使用 Mono.then() 完成非阻塞的 Mono 串行编排,替代错误的 block() 调用,实现“先发事件、再查数据、最后保存”的响应式链式流程。
本文讲解如何使用 `mono.then()` 完成非阻塞的 mono 串行编排,替代错误的 `block()` 调用,实现“先发事件、再查数据、最后保存”的响应式链式流程。
在基于 Project Reactor 的响应式编程中(如 Spring WebFlux),常见的误区是试图用同步思维组织异步逻辑——例如在 doOnSuccess 中直接调用 block() 等待下游 Mono 完成。这不仅违背响应式原则,更会在 I/O 线程(如 reactor-http-nio-2)上触发 IllegalStateException,报错提示:block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2。
根本原因在于:Reactor 的调度器线程(尤其是 HTTP 工作线程)是不可阻塞的。block() 会挂起当前线程,导致吞吐量骤降、连接超时甚至服务雪崩。
✅ 正确做法是利用 Reactor 提供的声明式组合操作符,以纯函数式方式表达依赖关系。针对“执行 A 后执行 B,并将 B 的结果作为最终输出”的场景,应使用 Mono.then(Mono
return spi.messageEvent(message)
.then(spi.getPersonById(evt.getVictimId()))
.flatMap(victim -> repository.save(victim)); // 注意:repository.save() 通常返回 Mono<victim></victim>
? 补充说明:
- then(Mono
) 会忽略上游 Mono 的值(即 messageEvent(...) 的 Void),仅等待其完成,然后订阅并返回下游 Mono ; - 若 repository.save(victim) 返回的是 Mono
(典型于 Spring Data R2DBC / MongoDB Reactive),请务必使用 flatMap 而非 map,否则会嵌套 Mono >; - 若 save 方法是阻塞式(如 JPA/Hibernate),则必须通过 publishOn(Schedulers.boundedElastic()) 切换至弹性线程池,绝不可在 NIO 线程上调用阻塞 API。
? 进阶建议:
- 使用 handle() 或 switchIfEmpty() 处理空值或异常分支;
- 为关键链路添加 .timeout(Duration.ofSeconds(5)) 防止无限等待;
- 通过 .checkpoint("fetch-and-save-person") 增强错误栈可读性。
总之,响应式编程的核心不是“如何等结果”,而是“如何描述数据流”。摒弃 block(),拥抱 then、flatMap、onErrorResume 等组合子,才能真正释放 Reactor 的高并发与低延迟优势。










