前言
本篇文章主要讲解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线程池会分配独立线程来完成业务处理, 并将结果返回给HandlerHandler收到worker线程池的结果之后, 通过send向客户端返回相应数据
一个
MainReactor可以对应多个SubReactor
优点
MainReactor线程与SubReactor线程的职责明确
MainReactor只负责接受新连接SubReactor只负责数据的业务处理
MainReactor线程与SubReactor线程的数据交互简单
MainReactor只需要将新数据传递给SubReactorSubReactor不需要将数据传递回给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中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)