1. 引言
本文将深入探讨 Spring MVC 中的 @Async
注解机制,并对比介绍 Spring WebFlux 的响应式编程模型。目标是帮助你清晰理解两者在异步处理、线程模型和 I/O 模式上的本质区别,避免在项目选型时“踩坑”。
2. 实现场景设计
为了直观对比,我们设计一个简单的 Web 接口场景:
- 单个接口返回字符串结果
- 请求经过一个 Filter,模拟 200ms 延迟
- Controller 处理耗时 500ms(模拟业务计算)
- 使用 Apache ab 发起并发请求(
-c 40
,共 1600 次) - 通过 JConsole 实时监控线程状态
⚠️ 注意:本文不是性能压测报告,重点在于观察线程调度行为和 I/O 模型差异。
3. Spring MVC Async 原理
Spring 3.0 引入 @Async
,用于将耗时任务提交到独立线程执行。从 Spring 3.2 起,结合 Servlet 3.0 的异步支持,@Async
可用于 @Controller
或 @RestController
,实现 Web 层异步化。
其核心流程如下:
✅ 请求进入 Filter 链
✅ 到达 DispatcherServlet
后,开启异步模式(调用 AsyncWebRequest#startAsync
)
✅ 将请求交给 WebAsyncManager
管理,Servlet 线程释放
✅ Filter 链反向执行(清理资源)
✅ WebAsyncManager
提交任务到 ExecutorService
✅ 任务完成,通知 DispatcherServlet
返回响应
返回类型必须是
Future
、CompletableFuture
或ListenableFuture
,不能是void
。
4. Spring Async 实现
主应用类
启用异步支持只需添加 @EnableAsync
:
@SpringBootApplication
@EnableAsync
public class AsyncVsWebFluxApp {
public static void main(String[] args) {
SpringApplication.run(AsyncVsWebFluxApp.class, args);
}
}
AsyncFilter
模拟 200ms 延迟:
@Component
public class AsyncFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
Thread.sleep(200); // 模拟处理耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
AsyncController
使用 @Async
返回 CompletableFuture
:
@RestController
public class AsyncController {
@GetMapping("/async_result")
@Async
public CompletableFuture<String> getResultAsync(HttpServletRequest request) {
try {
Thread.sleep(500); // 模拟业务耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Result is ready!");
}
}
测试结果
启动应用并执行压测:
ab -n 1600 -c 40 http://localhost:8080/async_result
JConsole 显示线程数明显上升,说明 @Async
使用了线程池来处理请求,属于 异步阻塞 I/O 模型。
5. Spring WebFlux 简介
Spring 5.0 推出 WebFlux,基于 Reactor 实现响应式编程,支持非阻塞 I/O 和响应式背压(reactive backpressure)。
关键特性:
✅ 支持 Servlet 3.1+ 的非阻塞 I/O
✅ 可运行在 Netty、Undertow、Tomcat、Jetty 等容器
✅ 基于 Reactor 的 Mono
和 Flux
提供声明式 API
✅ 支持函数式编程风格的接口定义
⚠️ 注意:WebFlux 的优势在于高并发、低延迟场景,对慢客户端更友好。
6. Spring WebFlux 实现
主应用类
无需额外注解,Spring Boot 自动识别 WebFlux 依赖:
@SpringBootApplication
public class AsyncVsWebFluxApp {
public static void main(String[] args) {
SpringApplication.run(AsyncVsWebFluxApp.class, args);
}
}
WebFluxFilter
实现 WebFilter
,使用 Mono.delay
模拟延迟:
@Component
public class WebFluxFilter implements org.springframework.web.server.WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
return Mono
.delay(Duration.ofMillis(200))
.then(webFilterChain.filter(serverWebExchange));
}
}
WebFluxController
返回 Mono<String>
,延迟 500ms 模拟处理:
@RestController
public class WebFluxController {
@GetMapping("/flux_result")
public Mono<String> getResult(ServerHttpRequest request) {
return Mono.defer(() -> Mono.just("Result is ready!"))
.delaySubscription(Duration.ofMillis(500));
}
}
测试结果
执行相同压测命令:
ab -n 1600 -c 40 http://localhost:8080/flux_result
JConsole 显示线程数稳定,仅使用少量事件循环线程,体现非阻塞特性。
7. 核心差异对比
特性 | Spring MVC Async | Spring WebFlux |
---|---|---|
I/O 模型 | 阻塞 | ✅ 非阻塞 |
请求体读取 | 阻塞 | ✅ 非阻塞 |
Filter/Servlet 模式 | 同步 | ✅ 全异步 |
支持容器 | Servlet 3.0+ | Servlet 3.1+、Netty、Undertow 等 |
背压支持 | ❌ | ✅ |
编程模型 | 命令式 + 异步 | ✅ 声明式 + 响应式 |
线程模型 | 线程池(每请求一线程) | 事件循环(少量线程处理高并发) |
关键结论
- Spring WebFlux 更适合 I/O 密集型、高并发场景,能以更少资源支撑更大吞吐
- Spring MVC Async 适合已有 MVC 项目做异步化改造,学习成本低,过渡平滑
- 若系统主要为 CPU 密集型任务,两者差异不大,传统 MVC + 线程池可能更简单粗暴
选型建议:不要为了“响应式”而响应式。现有系统稳定,QPS 不高,别轻易重构到 WebFlux,容易“画虎不成反类犬”。
8. 总结
本文通过实际代码和线程监控,对比了 Spring MVC Async 与 Spring WebFlux 在异步处理上的实现机制和行为差异。
@Async
是异步阻塞方案,依赖线程池,适合轻量级异步任务- WebFlux 是响应式非阻塞方案,基于事件驱动,适合高并发、低延迟场景
项目选型应结合团队技术栈、系统负载和运维复杂度综合判断。对于大多数中等规模系统,Spring MVC + @Async
完全够用;而对弹性伸缩、资源敏感型服务,WebFlux 才是更优解。
完整代码示例已托管至 GitHub: