Spring Boot 项目中的响应式应用(Reactive Web)与传统 MVC:原理区别、代码对比与适用场景
Spring Boot 项目中的响应式应用(Reactive Web)与传统 MVC:原理区别、代码对比与适用场景
在 Spring Boot 项目中,开发者经常需要在传统 Spring MVC 和响应式 WebFlux 之间做出选择,尤其当配置文件中出现 spring.main.web-application-type: reactive 时。本文将从底层原理、线程模型、I/O 处理方式、适用场景等角度详细对比两者,并通过实际代码示例说明差异。
1. 核心原理对比
| 维度 | 传统 Spring MVC (Servlet-based) | Spring WebFlux (Reactive / Non-blocking) |
|---|---|---|
| 编程范式 | 命令式(Imperative) | 声明式 + 响应式(Declarative + Reactive) |
| 底层 I/O 模型 | 阻塞 I/O(Blocking I/O) | 非阻塞 I/O(Non-blocking I/O) |
| 线程模型 | 线程-per-请求(每个请求独占一个线程) | 事件循环 + Reactor 线程池(少量线程处理大量连接) |
| 请求处理流程 | 请求 → 线程池分配线程 → 阻塞等待 I/O → 返回响应 | 请求 → 事件循环注册回调 → 非阻塞等待 → 回调执行 |
| 并发瓶颈 | 线程数上限(默认 200)→ 高并发时线程耗尽、上下文切换严重 | 线程数极少(默认 CPU 核数 × 2)→ 并发能力极高 |
| 资源利用率 | 线程阻塞时 CPU 空闲,资源浪费严重 | 线程不阻塞,CPU 利用率高,内存占用低 |
| 背压(Backpressure) | 无原生支持,高负载时容易雪崩 | 原生支持(Publisher 控制生产速度,避免下游崩溃) |
| 事件驱动来源 | Servlet 容器事件(Tomcat/Jetty) | Netty 事件循环 + Reactor 的 Scheduler |
| 异常处理 | 同步抛出异常,线程栈可追踪 | 异步异常通过 Mono/Flux 传播,栈追踪较复杂 |
传统 MVC(Servlet)原理简述
- 客户端请求到达 Tomcat/Jetty
- Servlet 容器从线程池取一个线程处理该请求
- 线程执行 Controller 方法
- 如果遇到数据库、网络 I/O,线程会阻塞等待(挂起)
- I/O 完成后继续执行,响应返回,线程归还线程池
- 高并发时线程池耗尽 → 请求排队 →