明白,你想深入了解 Spring Reactive / Netty 的线程模型 和 CPU 密集任务处理。我们逐条拆解,非常详细。
EventLoopGroup
├── EventLoop-1 (Thread-1)
├── EventLoop-2 (Thread-2)
└── EventLoop-3 (Thread-3)
一个 EventLoop 可以服务多个连接。
连接与 EventLoop 是固定绑定的:
当连接建立时,会分配到某个 EventLoop。
该连接的所有 I/O 事件都在这个 EventLoop 线程处理。
这样保证:
事件顺序
无锁处理单连接状态
高 CPU cache 命中
✅ 典型配置:
假设 4 核 CPU:
EventLoopGroup = 4 个 EventLoop(4 个线程)
每个 EventLoop 负责 1000 个连接
EventLoop 内部是单线程循环:
read → decode → handle → encode → write
保证顺序且无需锁
Connection-1 → EventLoop-1
Connection-2 → EventLoop-2
Connection-3 → EventLoop-1
Connection-4 → EventLoop-3
EventLoop 不能阻塞,阻塞会卡住整个 EventLoop,导致 QPS 崩盘。
Spring Reactive 提供了不同类型的 调度器:
Schedulers.boundedElastic() → 阻塞或 CPU 密集任务Schedulers.parallel() → CPU 密集计算Schedulers.single() → 单线程执行Mono.fromCallable(() -> heavyCompute())
.subscribeOn(Schedulers.parallel()) // 切到线程池
.map(result -> processResult(result));
Mono.fromCallable 在 EventLoop 外执行Mono.fromCallable(() -> blockingDBCall())
.subscribeOn(Schedulers.boundedElastic()) // 弹性线程池
subscribeOn / publishOn 控制线程切换⚠️ 核心原则:
EventLoop 只做非阻塞 I/O,CPU/阻塞任务切到线程池
| 概念 | Spring Reactive | Go goroutine |
|---|---|---|
| 并发单位 | Publisher / Subscriber | goroutine |
| I/O 处理 | EventLoop(单线程) | goroutine + runtime |
| CPU 密集 | Worker Pool / Scheduler | goroutine 阻塞安全 |
| 阻塞安全 | ❌ 直接阻塞 EventLoop 会卡 | ✅ goroutine 阻塞安全 |
| 多核利用 | 多 EventLoop + worker pool | runtime 自动 M:N 调度 |
| 编程复杂度 | 高,需要切 Scheduler | 低,顺序思维即可 |
Schedulers.parallel() 或 boundedElastic()Go:直接在 goroutine 中阻塞 CPU / I/O 都安全
Spring Reactive:阻塞 EventLoop = 灾难
Go 的优势:
心智负担低
并发几乎无限制
Spring Reactive:
强调“非阻塞 + 调度切换”
错误容易
学习曲线陡峭
✅ 总结一句话
Spring Reactive 的 EventLoop 固定处理多个连接,CPU 密集任务必须切线程池;
Go goroutine 可以直接阻塞,runtime 会自动调度线程,无需显式切线程池。
如果你愿意,我可以画一张图,直观对比 EventLoop + Worker Pool vs Go runtime 的 M:N 调度模型,一眼就能看懂两者的本质差异。
你想让我画吗?