Netty线程模型
背景
1. Java线程模型演进
单线程、多线程、线程池.
2. Reactor模型
无论是c++还是Java、nodejs 编写的网络框架, 大多数都是基于Reactor模型进行设计和开发.
Reactor模式基于事件驱动,特别适合处理海量的 IO事件.
(1) Reactor单线程模型.
由于reactor模式是 异步非阻塞IO, 所有的IO操作都不会导致阻塞.
理论上: 一个线程可以处理所有 IO相关的操作. 从架构层面看,一个 NIO线程 确实可以完成其承担的职责.
问题:
- 一个NIO线程同时处理成百上前的链路,性能上无法支撑。 瓶颈在于 海量消息的编码、解码、读取和发送(即便是NIO线程CPU负载100%).
- NIO线程负载过重之后,处理速度变慢,导致大量客户端连接超时。重发,更加重了NIO线程的负载,最终会导致大量消息积压和处理超时;
- 可靠性问题: 一旦NIO线程意外跑飞,或者进入死循环,系统通信模块直接不可用,直接故障。
(2) Reactor多线程模型
一组NIO线程处理IO操作【NIO操作指的是什么? read\write?】.
- 一个专门的NIO线程-Acceptor线程用于监听服务端,接受客户端的TCP连接请求
- 网络IO操作 - (读、写 等) 由一个NIO线程池负责【线程池可采用标准的JDK线程池实现,包含 一个任务队列和N个可用的线程, 这些NIO线程负责 消息的读取、解码、编码、发送】
- 一个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止并发操作问题.
重点: 大多数场景下, 此模型可以满足性能要求。 但是,极个别特殊场景, 一个Acceptor(NIO)线程 负责监听和处理所有的客户端连接可能存在性能问题。 例如: 并发百万客户端连接,或者服务端需要对客户端进行安全认证, 单独一个Acceptor线程 可能存在性能不足问题。
这时候,引入新的方式:Reactor 主从多线程模型。
Acceptor 接收到客户端 TCP 连接请求处理完成后(可能包含接入认证等),将新创建的 SocketChannel 注册到 IO 线程池(sub reactor 线程池)的某个 IO 线程上,由它负责 SocketChannel 的读写和编解码工作。Acceptor 线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端 subReactor 线程池的 IO 线程上,由 IO 线程负责后续的 IO 操作。
解决了 1 个服务端监听线程无法有效处理所有客户端连接的性能不足问题。
1 | 它的工作流程总结如下: |
Netty 线程模型
bossGroup 线程组实际就是 Acceptor 线程池,负责处理客户端的 TCP 连接请求,如果系统只有一个服务端端口需要监听,则建议 bossGroup 线程组线程数设置为 1。
workerGroup 是真正负责 I/O 读写操作的线程组,通过 ServerBootstrap 的 group 方法进行设置,用于后续的 Channel 绑定。
IO操作有哪些。
- OP_READ
- OP_WRITE
- OP_CONNECT
- OP_ACCEPT
Reactor是针对这些 事件 分发到不同的线程来进行处理.
问题:
作为服务端 Acceptor 线程,负责处理客户端的请求接入
boss线程和work线程的代码是一致的。 怎么区分? 是走的逻辑不同??
NioEventLoop设计原理
- 串行化设计避免线程竞争:串行执行Handler链.
- 定时任务与时间轮算法
- 聚焦而不是膨胀:只负责提供和管理NIO线程
1. 串行化设计避免线程竞争
2. 定时任务与时间轮算法
Netty中,很多功能依赖定时任务,比较典型的有2种:
- 客户端连接超时控制;
- 链路空闲检测
常见的设计理念是在 NioEventLoop中聚合JDK的 定时任务线程池 ScheduledExecutorService.
从性能角度看不是最优:原因:
- 线程上下文切换,打破了串行化设计理念
- 存在多线程并发问题. 定时Task和IO线程NioEventLoop可能同时访问并修改一份数据
- Jdk的sc..本身存在性能优化空间.
最早面临上述问题的是操作系统和协议栈,例如 TCP 协议栈,其可靠传输依赖超时重传机制,因此每个通过 TCP 传输的 packet 都需要一个 timer 来调度 timeout 事件。这类超时可能是海量的,如果为每个超时都创建一个定时器,从性能和资源消耗角度看都是不合理的。
时间轮算法调度: 根据 George Varghese 和 Tony Lauck 1996 年的论文《Hashed and Hierarchical Timing Wheels: data structures to efficiently implement a timer facility》提出了一种定时轮的方式来管理和维护大量的 timer 调度。Netty 的定时任务调度就是基于时间轮算法调度,下面我们一起来看下 Netty 的实现。