前言
本篇文章主要讲解Reactor
模型, Reactor
线程模式经典的很, 但是还是有很多小伙伴不知道, 接下来我们一起学习一下Netty
的基础——Reactor模型
Reactor 模型特性
Reactor
模型是基于事件驱动的, 通过一个或多个输入同时传递给服务端处理- 服务端程序处理传入的多个请求, 并发到相应的处理程序
- 基于 IO多路复用技术, 多个连接同时共用一个多路复用器, 应用程序只需在一个阻塞对象等待, 无需阻塞等到所有连接, 当某个连接上有新数据可以处理时, 应用程序的线程从阻塞状态返回, 开始处理这个连接上的业务
- 基于线程池技术复用线程资源: 不必为每个连接创建专用的线程, 应用程序将连接上的业务处理任务分配给线程池中的线程进行处理, 一个线程可以处理多个连接的业务
传统 BIO 服务
在传统的 BIO服务端采用的是单线程单连接
的处理模型, 这种线程模型的弊端很明显, 当有大量的客户端并发时服务端会压力剧增, 同时线程利用率很低
网络异常,图片无法展示
|
相应的, 在传统BIO服务上的Reactor
模型的基本形态如下图所示
网络异常,图片无法展示
|
单 Reactor 单线程模型
网络异常,图片无法展示
|
图片来源于网络
这种模式的工作流程如下:
Reactor
监听客户端请求事件, 收到事件之后通过dispatch
进行分发- 如果事件是建立连接的请求事件, 则由
Acceptor
通过accept
处理连接请求, 然后创建一个Handler
对象处理完连接之后的后续业务处理 - 如果不是建立连接的请求事件, 则由
Reactor
分发给对应的Handler
处理
Handler
只负责响应事件, 不做具体的业务处理操作
- Handler处理流程:
- read 读取数据
- worker 线程池处理
- 处理之后返回给 Handler
- send 发送结果返回给 client
优点
- 模型简单
- 没有多线程, 进程通信, 竞争的问题
缺点
- 存在性能问题, 只有单线程, 无法完全发挥 CPU 的性能, 在
Handler
处理某个连接上个业务的时候, 无法处理其他连接事件, 容易导致性能瓶颈 - 存在可靠性问题, 若线程意外终止或者进入死循环, 则整个系统都将不可用, 不能接收和处理消息
单 Reactor 多线程模型
网络异常,图片无法展示
|
图片来源于网络
这种模式的工作流程如下:
Reactor
对象监听连接事件, 收到事件后通过dispatch
进行分发- 如果事件
是
建立连接的请求事件,则由acceptor
接收连接, 然后创建一个Handler
对象处理连接建立后的业务 - 如果事件
不是
建立连接的请求事件,则由Reactor
对象分发给连接对应的Handler
处理 worker
线程池会分配独立线程来完成真正的业务处理, 并将处理结果返回给Handler
,Handler
通过Send
向客户端发送相应数据
优点
充分利用了多核CPU
的处理能力
缺点
多线程数据共享和控制比较复杂, Reactor
处理所有的事件监听和响应都在单线程内运行, 还是很容易出现性能瓶颈
主从 Reactor 多线程模型
主从Reactor模型
的核心思想: 主反应堆栈只负责分发Acceptor
的连接建立, 已连接套接字上的IO事件
交给SubReactor
负责分发, 其中SubReactor
的数量可以根据 CPU核心的数量灵活配置
网络异常,图片无法展示
|
图片来源于网络
工作流程
Reactor
对象监听连接事件, 收到事件后通过Acceptor
处理客户端连接事件- 当
Acceptor
处理完客户端连接事件之后,MainReactor
将连接分配给SubReactor
SubReactor
将连接加入自己的连接队列进行监听, 并创建Handler
对事件进行处理- 当连接上有新的事件发生,
SubReactor
就会调用相应的Handler
进行处理 Handler
通过read
从连接上读取数据, 并分发给worker
线程池进行处理worker
线程池会分配独立线程来完成业务处理, 并将结果返回给Handler
Handler
收到worker
线程池的结果之后, 通过send
向客户端返回相应数据
一个
MainReactor
可以对应多个SubReactor
优点
MainReactor
线程与SubReactor
线程的职责明确
MainReactor
只负责接受新连接SubReactor
只负责数据的业务处理
MainReactor
线程与SubReactor
线程的数据交互简单
MainReactor
只需要将新数据传递给SubReactor
SubReactor
不需要将数据传递回给MainReactor
, 直接将数据返回给客户端
- 多个
SubReactor
能够应对更高的并发请求
缺点
编程负责度较高
Netty 的 Reactor 模型
网络异常,图片无法展示
|
图片来源于网络
Netty
有两组线程池, 分别是BossNioEventLoopGroup
和WorkerNIOEventLoopGroup
, 每个线程池都是一组NioEventLoop
,BossNioEventLoopGroup
主要负责与客户端建立连接,workerNioEventLoopGroup
主要负责处理连接上的读写NioEventLoopGroup
相当于一个事件循环组, 在这个组内有很多个NioEventLoop
死循环处理事件的线程, 每个NioEventLoop
都包含一个Selector
, 用于监听Channel
- 每个事件循环组
BossNioEventLoopGroup
都会执行以下三个步骤
select:
轮询注册在SererSocketChannel
上的appect
事件processSelectedKeys:
处理appect
事件, 与客户端建立连接, 生成一个NioSocketChannel
, 并将其注册到某一个NioEventLoop
的Selector
上runAllTasks:
再去循环处理任务队列中的其他任务
- 每个事件循环组
WorkerNioEventLoopGroup
都会执行以下三个步骤
select:
轮询注册在SererSocketChannel
上的read
事件和write
事件processSelectedKeys:在对应的
Channel上处理
read事件和
write`事件runAllTasks:
再去循环处理任务队列中的其他任务
- 在上面两个
processSelectedKeys
步骤中,会使用Pipeline
管道),Pipeline
中引用了Channel
,即通过Pipeline
可以获取到对应的Channel
,Pipeline
中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)