线程模型与Netty

线程模型与 Netty

传统 Servlet 线程模型

传统 Spring MVC 基于 Servlet 容器,采用每请求一线程模型:

1
2
3
请求1 ──→ 线程1 ──→ [接收请求 → 处理业务 → 等待DB → 返回响应] ──→ 释放
请求2 ──→ 线程2 ──→ [接收请求 → 处理业务 → 等待DB → 返回响应] ──→ 释放
请求3 ──→ 线程3 ──→ [接收请求 → 处理业务 → 等待DB → 返回响应] ──→ 释放

特点

  • 一个请求独占一个线程,从头到尾
  • 线程在等待 I/O(数据库、网络调用)时阻塞,什么都不做
  • 高并发时需要大量线程(Tomcat 默认 200 个)
  • 线程切换开销大,内存占用高(每线程约 1MB 栈空间)

WebFlux 事件循环模型

WebFlux 基于 Netty,采用事件循环(Event Loop) 模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
                 ┌─────────────────────────────────────┐
Event Loop 线程池 │
(少量线程,如 CPU 核心数)
└─────────────────────────────────────┘

┌───────────────────────────┼───────────────────────────┐
↓ ↓ ↓
接收请求1 接收请求2 接收请求3
│ │ │
↓ ↓ ↓
提交I/O任务 提交I/O任务 提交I/O任务
(非阻塞返回) (非阻塞返回) (非阻塞返回)
│ │ │
└───────────────────────────┼───────────────────────────┘

┌─────────────────────────────────────┐
I/O 完成后触发回调 │
Event Loop 继续处理 │
└─────────────────────────────────────┘

核心思想

  • 线程不等待 I/O 完成,立即去处理其他请求
  • I/O 完成后通过回调/事件通知
  • 少量线程就能处理大量并发连接

WebFlux 中的线程池

WebFlux 使用多个线程池,各司其职:

1. Event Loop 线程池

1
2
// Netty 的 NioEventLoopGroup
// 线程数 = CPU 核心数
  • 处理连接建立、读写事件
  • 绝对不能阻塞,否则影响所有请求

2. boundedElastic 线程池

1
2
Mono.fromCallable(() -> blockingCall())
.subscribeOn(Schedulers.boundedElastic())
  • 处理必须阻塞的操作(如调用阻塞 API)
  • 弹性伸缩,有上限保护
  • 适合 I/O 密集型阻塞操作

3. parallel 线程池

1
2
3
Flux.range(1, 100)
.parallel()
.runOn(Schedulers.parallel())
  • 线程数 = CPU 核心数
  • 适合 CPU 密集型任务

线程模型对比

方面 传统 MVC WebFlux
线程数量 大量(默认 200+) 少量(CPU 核心数)
等待 I/O 时 线程阻塞等待 线程去处理其他请求
线程利用率 低(大量时间在等待) 高(几乎不等待)
内存占用 高(每线程 ~1MB)
上下文切换 频繁 很少
编程模型 同步阻塞 异步非阻塞

WebFlux 与 Netty 的关系

Netty 是什么

Netty 是一个高性能网络通信框架,提供:

  • 非阻塞 I/O(NIO)
  • 事件驱动模型
  • 高效的内存管理
  • 协议编解码支持

直接用 Netty

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Netty 原生代码 - 处理一个简单的 HTTP 请求
public class HttpServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;

// 手动解析路径
String uri = request.uri();

// 手动构建响应
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer("Hello", CharsetUtil.UTF_8)
);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,
response.content().readableBytes());

ctx.writeAndFlush(response);
}
}
}

需要自己处理:路由、参数解析、JSON 序列化、异常处理、连接管理…

用 WebFlux

1
2
3
4
5
6
7
8
@RestController
public class HelloController {

@GetMapping("/hello/{name}")
public Mono<User> hello(@PathVariable String name) {
return userService.findByName(name); // 自动 JSON 序列化
}
}

WebFlux 在 Netty 之上提供了什么

功能 Netty WebFlux
路由 手动解析 URI @GetMappingRouterFunction
参数绑定 手动解析 @PathVariable@RequestBody
JSON 序列化 手动处理 自动(Jackson)
异常处理 手动 try-catch @ExceptionHandler
依赖注入 Spring IoC
配置管理 application.yml
安全认证 手动实现 Spring Security
数据访问 手动实现 Spring Data R2DBC
测试支持 手动 WebTestClient

类比理解

1
2
3
4
5
6
Netty      ≈  TCP/HTTP 协议引擎(底层)
WebFlux ≈ Web 开发框架(上层)

就像:
JDBC ≈ 数据库连接(底层)
MyBatis ≈ ORM 框架(上层)

Netty 是引擎,WebFlux 是汽车。 你可以直接用引擎,但大多数人更愿意开汽车。

什么时候直接用 Netty

  • 自定义协议(非 HTTP)
  • 极致性能优化
  • 不需要 Spring 生态
  • 构建基础设施(如网关、代理服务器)

为什么 WebFlux 适合高并发

  1. 少量线程处理大量连接:不需要为每个连接分配线程
  2. 线程不阻塞:I/O 等待时去处理其他请求
  3. 内存效率高:线程少,内存占用低
  4. 无线程切换开销:事件驱动,不需要频繁切换

相关链接

  • Webflux简介
  • 响应式编程基础
  • Reactor核心API

线程模型与Netty
https://zmmmmy.github.io/2026/01/10/线程模型与Netty/
作者
ZhiMy
发布于
2026年1月10日
许可协议