Netty 的高吞吐来自少量 EventLoop 快速推进大量连接,不是因为它能让慢业务自动变快。只要一个处理器阻塞,归属同一 EventLoop 的其他连接都会增加延迟。
Table of contents
Channel 为什么固定在一个 EventLoop
同一 Channel 的事件由固定 EventLoop 串行处理,处理器可以减少连接内状态的锁竞争。跨线程调用 channel.writeAndFlush 时,Netty 会把实际操作安排回所属 EventLoop。
EventLoop 通常轮流处理 I/O 事件和任务队列。一个任务运行太久,定时任务、读取和写完成通知都会延后。因此“线程没有死锁”并不代表事件循环健康。
哪些工作必须卸载
- 阻塞式数据库或 HTTP 客户端调用;
- 文件系统访问和不可控 DNS 查询;
- 大对象压缩、加密或复杂计算;
- 可能等待锁的旧代码;
- 任何延迟上界无法保证的第三方 SDK。
任务卸载样例下载把慢任务交给单独执行器:
mvn -Dtest=NetworkServerLabTest#offloadsSlowWork test
样例使用虚拟线程便于表达阻塞任务,但生产环境仍必须限制并发,因为数据库连接、远端配额和内存不会随虚拟线程增加。
卸载不是无限提交
执行器要有明确所有者、并发上限、排队上限、拒绝策略和关闭流程。请求离开 EventLoop 前记录截止时间;轮到执行时如果截止时间已过,直接取消,不再消耗下游资源。
响应完成后还要检查 Channel 是否仍然活跃、请求是否已取消。多个请求允许乱序响应时用请求 ID 关联;协议要求顺序时必须显式排序,不能依赖任务恰好按提交顺序结束。
测量事件循环积压
平均接口耗时会掩盖短暂卡顿。至少观测:
| 指标 | 说明 |
|---|---|
| 事件循环延迟 | 定时任务实际执行时间减计划时间 |
| 待执行任务数 | 是否持续积压 |
| Handler 执行分位数 | 哪个处理阶段变慢 |
| Channel 不可写时间 | 出站是否堵塞 |
| 工作执行器队列与拒绝 | 卸载层是否饱和 |
压测时同时制造慢下游、半包、慢客户端和连接关闭,观察系统是否有界退化。只用本机持续发送小包,无法证明生产稳定性。
线程模型的选择
短小非阻塞协议处理留在 EventLoop;可控 CPU 任务交给固定大小池;大量阻塞 I/O 可评估有并发门控的虚拟线程。选择依据是工作性质和下游容量, 不是“线程越少越先进”或“虚拟线程越多越快”。
下一步
阅读Netty 内存池、引用计数与泄漏排查,理解高吞吐缓冲区怎样安全回收。