Skip to content

概念卡片:Netty线程模型与高性能

一句话机制:Netty 高性能 = I/O 模型(NIO 多路复用)+ 线程模型(Reactor 主从)+ 内存效率(零拷贝 + 内存池)+ 可靠性(粘包解码/心跳/Selector bug 修复);其中线程模型的核心是 bossGroup 只收连接、workerGroup 管读写,业务在单线程串行执行避免锁竞争

BIO / NIO / AIO 区别(必考)

维度BIONIOAIO
模型一连接一线程一请求一线程(多路复用)一有效请求一线程
阻塞阻塞(等数据)非阻塞(立即返回状态)非阻塞(内核完成后回调)
面向流(Stream 单向)缓冲区(Channel 双向)缓冲区
适用连接少、低并发高并发、连接多连接数多且长耗时
  • 伪异步 IO:BIO + 线程池,请求入队,缓解线程开销但仍受线程池限制。
  • NIO 三组件:Channel(通道,双向)/ Buffer(缓冲区,读写中转)/ Selector(选择器,单线程管多 Channel)。
  • Buffer 关键方法:flip() 写→读切换(position 给 limit,position 置 0);clear() 读→写(position 置 0,limit=capacity);rewind() 重读。
  • Selector 监听事件:读 / 写 / 连接 / accept;Linux 实现是 EPollSelectorImpl(epoll_ctl 注册,fdToKey 维护 fd↔SelectionKey 映射)。

Reactor 模型三变种

Reactor = while(true) { selector.select(); 分发事件 } 的反应堆线程,两要素:Reactor(监听分发)+ Handler(实际处理)。

变种组成局限
单 Reactor 单线程一个线程干所有(accept/read/decode/process/encode/send)高负载不行;线程死循环整个服务挂
单 Reactor 多线程Reactor 收连接,线程池处理读写 + 业务百万连接时一个 Acceptor 成瓶颈
主从 Reactor 多线程MainReactor 收连接(认证/握手),SubReactor 池处理读写,业务线程池处理非 IONetty 采用此模型的变体

Netty 线程模型落地(boss / worker)

BossGroup(MainReactor)→ accept 事件 → 封装 NioSocketChannel → 注册到 WorkerGroup
WorkerGroup(SubReactor + Worker 同池)→ read/write 事件 → Handler 处理
  • bossGroup:只在 bind 后拿一个线程当 MainReactor,专收 accept(每个端口对应一个 boss 线程)。
  • workerGroup:被各 SubReactor 和 worker 线程复用;默认线程数 CPU 核数 × 2Math.max(1, cpu*2)),bind 完才启动。
  • 注意:Netty 虽借用主从 Reactor 结构,但实际 SubReactor 与 Worker 在同一个线程池里(不是分离的两个池)。
  • 每个 NioEventLoop 循环 3 步:轮询事件 → 处理 I/O 事件(processSelectedKeys)→ 处理任务队列(runAllTasks,每 64 个任务检查耗时防阻塞 I/O)。

粘包/拆包

原因:TCP 是流式协议——写入 > 发送缓冲区 → 拆包;多次小写入合并 → 粘包;MSS 分段 / MTU(1500B) IP 分片。

四种内置解码器

解码器原理
FixedLengthFrameDecoder定长消息
LineBasedFrameDecoder换行符分隔
DelimiterBasedFrameDecoder自定义分隔符
LengthFieldBasedFrameDecoder消息头长度字段(最通用)

零拷贝(四层面)

  1. Direct Buffer:堆外直接内存读写 Socket,免去「堆内存 → 直接内存」一次拷贝。
  2. CompositeByteBuf:多个 ByteBuf 逻辑合并,避免物理拷贝拼大 Buffer。
  3. FileRegion + transferTo:文件直接发到目标 Channel,避免循环 write 的内存拷贝。
  4. wrap():byte[] / ByteBuffer 包装成 ByteBuf 零拷贝。

Selector 空轮询 Bug(epoll bug)

  • 现象:select 轮询结果为空仍持续空转,CPU 100%;JDK 6u18 声明修复但 1.7 仍存在。
  • Netty 解法:selectCnt 记录空轮询次数,连续空转达到阈值(默认 512)→ rebuildSelector():新建 Selector → cancel 旧 key → Channel 重新注册 → 关闭旧 Selector。

心跳机制

  • TCP 自带 SO_KEEPALIVE灵活性不够(只探测死链,不灵活),实际在应用层做自定义心跳
  • Netty 用 IdleStateHandlerreaderIdleTime(读超时)/ writerIdleTime(写超时)/ allIdleTime(总超时);空闲发 PING-PONG 保活。
  • 服务端用:定时清除闲置会话;客户端用:检测断线/网络延迟。

不变量(必须成立的约束)

  • 1 个 EventLoop = 1 线程 + 1 Selector;一个 Channel 只绑定一个 EventLoop,保证线程安全。
  • boss 只收连接不读数据,worker 只读写不收连接
  • 业务 Handler 中禁止长时间阻塞——会卡死所属 EventLoop 线程(该线程所有 Channel 一起遭殃)。

常见误解

  • 以为零拷贝是"完全不用拷贝" → 是减少用户态↔内核态拷贝次数(Direct Buffer / transferTo 把数据留在内核态直传)。
  • 以为 Netty 的 worker 是"每连接一线程" → 一个 worker 线程管成百上千个 Channel。
  • 以为粘包是 Netty 的问题 → 是 TCP 流式协议的天然现象,Netty 只是提供了现成解码器。

关联

最近更新