Netty线程模型
前言
接着我们学习一下 Netty 的线程模型,了解了 Netty 的线程模型之后我们对 Netty 的整体架构也就有了一个大致的了解。
由于 Netty 的线程模型是基于 Reactor 模型改进而来的,因此先讲讲 Reactor 模型,有助于我们对 Netty 线程模型的理解 。
一、Reactor 模型
The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.
Reactor 模式也叫做反应器设计模式,是一种为处理服务请求并发提交到一个或者多个服务处理器的事件设计模式
Reactor 模型是指当服务器接收到多个请求时,服务器程序会把它们分派到不同的方法或线程去处理。Reactor 模式也被称作 Dispatcher 模式。它的核心是多路复用器,多路复用器收到事件后会进行分发,这点是网络服务器高并发的关键。
三种角色:
- Reactor:派发器,负责监听和分配事件,并将事件分派给对应的 Handler。新的事件包含连接建立就绪、读就绪、写就绪等。
- Acceptor:请求连接器,处理客户端新连接。Reactor 接收到 client 端的连接事件后,会将其转发给 Acceptor,由 Acceptor 接收 Client 的连接,创建对应的 Handler,并向 Reactor 注册此 Handler。
- Handler:请求处理器,负责事件的处理,将自身与事件绑定,执行非阻塞读/写任务,完成 channel 的读入,完成处理业务逻辑后,负责将结果写出 channel。可用资源池来管理。
Reactor 模型分为四种:
- 单 Reactor 单线程
- 单 Reactor 多线程
- (主从)多Reactor单线程
- (主从)多 Reactor 多线程
这三种模型按顺序来看理解起来复杂度不断提升,也会更接近 Netty 的线程模型,下面来分别看看这三种模型。
1.1 单 Reactor 单线程
这个最好理解,只有一个线程,只是会把建立连接和处理请求这两种任务分发给不同的类去处理,如下图所示:
- Reactor 线程通过 select 监听事件,收到事件后通过 Dispatch 进行分发
- 如果是连接建立事件,则将事件分发给 Acceptor,Acceptor 会通过 accept() 方法获取连接,并创建一个Handler 对象来处理后续的响应事件
- 如果是IO读写事件,则 Reactor 会将该事件交由当前连接的 Handler 来处理
- Handler 会完成 read -> 业务处理 -> send 的完整业务流程
优点
- 模型简单,不会有多线程的那些问题
缺点
- 性能问题:单线程无法发挥多核 CPU 的性能
- 可靠性问题:处理业务时往往容易出问题,当 Handler 出问题了,由于只有一个线程,整个节点也挂了
1.2 单 Reactor 多线程
这个线程模型针对前面的问题作出了一定的优化,多出了处理业务的线程池,如下图所示:
- Reactor 线程通过 select 监听事件,收到事件后通过 Dispatch 进行分发
- 如果是连接建立事件,则将事件分发给 Acceptor,Acceptor 会通过 accept() 方法获取连接,并创建一个Handler 对象来处理后续的响应事件
- 如果是IO读写事件,则 Reactor 会将该事件交由当前连接对应的 Handler 来处理
- 与单Reactor单线程不同的是,Handler 不再做具体业务处理,只负责接收和响应事件,通过 read 接收数据后,将数据发送给后面的 Worker 线程池进行业务处理。
- Worker 线程池再分配线程进行业务处理,完成后将响应结果发给 Handler 进行处理。
- Handler 收到响应结果后通过 send 将响应结果返回给 Client。
优点
- 可以充分利用多核 CPU 的处理能力
缺点
- 多线程资源共享和访问处理会比较复杂,在主线程处理所有的连接、监听和响应也会出现性能瓶颈
1.3 主从 Reactor 多线程
主从 Reactor 多线程模型又在前面的模型基础上做了进一步优化,增加了子 Reactor ,如下图所示:
整个流程大概可以分为以下几步:
- 主线程的
MainReactor
负责监听连接请求,收到连接请求会由 Acceptor 进行处理,成功建立连接之后MainReactor
会把连接分派给SubReactor
,由SubReactor
监听和处理数据请求; SubReactor
监听到数据请求,会派发给 Handler 处理,Handler 只会处理读取数据和发送数据部分,中间业务处理部分也是放在线程池中完成。
优点
MainReactor
与SubReactor
职责分明,一个处理连接事件,一个处理数据请求;MainReactor
与SubReactor
交互逻辑比较简单,MainReactor
单向地将建立好的连接传递出去;- 多 Reactor 设计能在高并发场景拥有更好的性能。
缺点
- 编程复杂度较高
主从 Reactor 多线程模式是业界非常成熟的服务器程序设计模式,在很多中间件中都使用到了这种模式,像 Nginx、Memcached、Netty 等。这种模式也被称为 1 + M + N 模式,分别代指相对少的连接线程(不一定为 1 ),多个 I/O 线程和多个业务处理线程。
二、Netty 线程模型
Netty 线程模型是基于主从 Reactor 多线程模型优化而来的,整体架构如下图所示:
Netty 的线程模型主要分为两部分,分别是 BossGroup
和 WorkerGroup
,它们都分别管理一个或多个 NioEventLoop
。每个 NioEventLoop
对应着一个线程,一个 Selector,一个 Executor 和一个 TaskQueue
。
NioEventLoop
可以理解成一个事件循环,当程序启动后每个 NioEventLoop
都会通过 Executor 启动一个线程,开始执行事件循环,在循环中 Selector 会通过 select 方法阻塞并监听就绪事件,当有事件到来时通过 processSelectedKeys
方法处理 Selector 事件,之后再通过 runAllTasks
方法处理其他的任务。
与前面介绍的 主从 Reactor 多线程模型类似,BossGroup
负责连接事件,当建立连接之后会生成一个 NioSocketChannel
并注册到 WorkGroup
其中一个 NioEventLoop
的 Selector 上。WokerGroup
中的 NioEventLoop
负责处理数据请求,当请求到来时会调用 processSelectedKeys
方法,其中的业务处理会依次经过 Pipeline 中的多个 Handler。