Netty(四)-线程模型
参考文献
- Scalable IO in Java --Doug Lea
线程模型
- 基本的线程池化模式可以描述为
- 从池的空闲线程列表中选择一个Thread,并且指派它去运行一个已提交的任务(一个Runnable的实现);
- 当任务完成时,将该Thread返回给该列表,使其可被重用。
Reactor
线程模式
-
Reactor
线程模式是一种基于事件驱动的编程模式. -
它将事件的处理分解为两个基本操作:事件的分发和事件的处理.
-
Reactor
模式通常包括以下角色:-
Reactor
:Reactor
是整个模式的核心,它负责监听和分发事件,将事件分发给相应的处理器进行处理.Reactor
通常使用事件循环来轮询事件队列,将事件分发给相应的处理器. -
Handler
:Handler
是事件处理器,负责处理具体的事件.当Reactor
将事件分发给Handler
时,Handler
会执行相应的业务逻辑,并将处理结果返回给Reactor
. -
Acceptor
:Acceptor
是连接处理器,负责处理新的连接请求.当有新的连接请求到来时,Acceptor
会创建一个新的连接,并将其注册到Reactor
中. -
Connector
:Connector
是连接器,负责建立与远程服务器的连接.当需要与远程服务器通信时,Connector
会创建一个新的连接,并将其注册到Reactor
中. -
Dispatcher
:Dispatcher
是事件分发器,负责将事件分发给相应的处理器.Dispatcher
通常使用线程池来管理处理器线程,可以避免线程创建和销毁的开销,提高并发性能和可伸缩性.
-
Reactor
的分类
单Reactor
单线程模式
- 单
Reactor
单线程模式:整个应用程序只有一个Reactor
线程和一个事件循环来处理所有的事件,适合于低并发和短时间任务的场景. - 当有新的事件到来时,该事件会被放入到事件队列中,
Reactor
线程会轮询该队列,并将事件分发给相应的处理器进行处理. - 这种模式的优点是简单、易于实现,但并发性能受限,不能充分利用多核 CPU 的优势,适用于对性能要求不高的场景,如 Redis 单线程模式.
单Reactor
多线程模式
- 单
Reactor
多线程模式:整个应用程序只有一个Reactor
线程和多个工作线程来处理所有的事件,适合于高并发和长时间任务的场景. - 当有新的事件到来时,该事件会被放入到事件队列中,
Reactor
线程会轮询该队列,并将事件分发给空闲的工作线程进行处理. - 这种模式的优点是可以利用多核 CPU 的优势,提高并发性能和可伸缩性,但需要处理线程之间的同步和数据共享问题,适用于对性能要求高的场景,如 Netty 多线程模式.
主从Reactor
多线程模式
- 主从
Reactor
多线程模式:整个应用程序有一个主Reactor
线程和多个从Reactor
线程来处理所有的事件,适合于高并发和长时间任务的场景. - 当有新的事件到来时,主
Reactor
线程会将事件分发给从Reactor
线程进行处理,从Reactor
线程可以使用多线程模式来处理事件. - 这种模式的优点是可以有效避免锁竞争和线程之间的同步问题,提高并发性能和可伸缩性,适用于对性能和可靠性要求高的场景,如 Nginx主从
Reactor
多线程模式.
Netty
的Reactor
线程模式
Netty
推荐采用主从多线程模型.Reactor
线程模型运行机制的四个步骤- 连接注册:
Channel
建立后,注册至Reactor
线程中的Selector
选择器 - 事件轮询: 轮询
Seletor
选择器中已注册的所有Channel
的I/O
事件 - 事件分发: 为准备就绪的
I/O
事件分配相应的处理线程 - 任务处理:
Reactor
线程还负责任务队列中的非I/O
任务,每个Worker
线程从各自维护的任务队列中取出任务异步执行.
- 连接注册:
Reactor角色 | Netty的组件 |
---|---|
Reactor |
NioEventLoop |
Handler |
ChannelHandler 接口实现类 |
Acceptor |
ServerBootstrap 和ServerChannelInitializer |
Connector |
ChannelInitializer |
Dispatcher |
EventExecutorGroup 和ThreadPoolExecutor |
NioEventLoop
负责监听和分发事件ServerBootstrap
和Bootstrap
用于创建服务器和客户端连接ChannelHandler
处理事件EventExecutorGroup
和ThreadPoolExecutor
管理处理器线程和线程池
-
BossEventLoopGroup
和WorkerEventLoopGroup
包含一个或者多个NioEventLoop
.BossEventLoopGroup
负责监听客户端的Accept
事件,当事件触发时,将事件注册至WorkerEventLoopGroup
中的一个NioEventLoop
上.每新建一个Channel
,只选择一个NioEventLoop
与其绑定.即Channel
生命周期的所有事件处理都是线程独立的,不同的NioEventLoop
线程之间不会发生任何交集. -
NioEventLoop
完成数据读取后,会调用绑定的ChannelPipeline
进行事件传播,ChannelPipeline
也是线程安全的,数据会被传递到ChannelPipeline
的第一个ChannelHandler
中.数据处理完成后,将加工完成的数据再传递给下一个ChannelHandler
,整个过程是串行化执行的,不会发生线程上下文切换的问题. -
NioEventLoop
无锁串行化的设计不仅使系统吞吐量达到最大化,而且降低了用户开发业务逻辑的难度,不需要花太多精力关心线程安全问题.虽然单线程执行避免了线程切换,但是它的缺陷就是不能执行时间过长的I/O操作,一旦某个I/O事件发生阻塞,那么后续的所有I/O事件都无法执行,甚至造成事件积压.在使用Netty
进行程序开发时,一定要对ChannelHandler
的实现逻辑有充分的风险意识.如果ChannelHandler逻辑非常复杂而且是阻塞的,那么请用
ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);
ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);
- 如果你的项目无需考虑
ChannelHandler
的执行顺序,可以用UnorderedThreadPoolEventExecutor
这个线程组以达到最高的并发度