framework
framework/webflux/when-reactive.md
medium
webflux-when-reactive
WebFlux 何时该用何时别用 — IO 密集高并发/流式收益大;CPU 密集/团队不熟/全栈未响应式化收益有限,与虚拟线程取舍对比。Use when 评估上不上 WebFlux / 选响应式还是虚拟线程 / MVC 还是 WebFlux 时。
响应式选型webflux 适用场景reactive vs 虚拟线程io 密集高并发流式全栈响应式
paths
*.java
Spring WebFlux · 何时用 / 何时别用响应式
本条只回答「该不该上 WebFlux」。基础语法见
mono-flux.md。响应式 vs 虚拟线程的更底层对比见when-to-use.md。
收益从哪来
WebFlux 基于事件循环 + 非阻塞 IO,少量线程承载大量并发连接,收益只来自"省下被阻塞 IO 占住的线程"。所以判据和虚拟线程同源:这段是 IO 等待还是 CPU 计算。但 WebFlux 是全链路非阻塞才成立——任一环节阻塞就退化甚至更糟。
规则
| 场景 | 用不用 | 原因 |
|---|---|---|
| IO 密集 + 高并发:网关、聚合多个下游、海量长连接 | ✅ 用 | 少量线程扛高并发,省下阻塞占用的线程 |
| 流式 / SSE / 大数据集逐条推:数据流、实时推送 | ✅ 用 | 背压天然支持,逐元素流式输出 |
| CPU 密集:加解密、大量计算 | ❌ 不用 | 事件循环线程被算占住会拖垮全局,受限核数 |
| 团队不熟响应式 / 难调试 | ❌ 慎用 | 心智负担高、堆栈断裂难排错,维护成本盖过收益 |
| 全栈未响应式化(仍用 JDBC/阻塞客户端) | ❌ 收益有限 | 链路里有阻塞环节,非阻塞优势被抵消 |
与虚拟线程的取舍
| 维度 | WebFlux 响应式 | 虚拟线程(JDK21) |
|---|---|---|
| 编程模型 | 声明式操作符链,全链路非阻塞 | 同步阻塞写法,JVM 自动卸载 |
| 学习/调试成本 | 高,堆栈断裂 | 低,普通堆栈 |
| 背压 | 内建支持 | 不内建,需自管 |
| 改造范围 | 需全栈非阻塞(R2DBC/WebClient) | 阻塞代码几乎不改 |
| 选谁 | 已响应式全栈 / 需背压与流式 | 只想吃 IO 并发红利、不想改写法 |
一句话:只为提高 IO 并发,优先虚拟线程(改动小);需要流式/背压/已是响应式全栈,才上 WebFlux。
反例
❌ 一个纯 CRUD、底层还是 JDBC 的内部后台,为"性能"全量迁 WebFlux
—— 链路有阻塞环节,收益被抵消,反而背上响应式调试成本。
❌ 报表导出(大量内存计算)放进 WebFlux 事件循环线程 —— 拖垮整个实例。
自检
- [ ] 确认是 IO 密集且高并发 / 需要流式与背压,才选 WebFlux?
- [ ] 已评估"虚拟线程改动更小是否更划算"(见跨模块链接)?
- [ ] 全链路都能非阻塞(R2DBC + WebClient),没有残留 JDBC/阻塞客户端?
- [ ] 团队能承担响应式的调试与维护心智成本?
相关
- 父:
./index.md - 兄弟:
mono-flux.md(决定用了之后的基础语法) - 兄弟:
blocking-trap.md("全栈非阻塞"具体怎么落地) - 跨模块:
when-to-use.md(虚拟线程何时用,与响应式取舍)