线程与 IO 模型
{Back to Index}
Table of Contents
1 EventLoop
EventLoop 本质是一个单线程执行器,内部维护一个 Selector 对象,通过 run 方法处理 Channel 上源源不断的 IO 事件。
EventLoopGroup 则是一组 EventLoop 的集合,Channel 会通过 EventLoopGroup::register
绑定至其中一个 EventLoop ,后续所有关于此 Channel 的 IO 事件都由该 EventLoop 负责处理, 这样保证了 IO 处理的线程安全性。
常见的 EventLoopGroup 实现有:
- NioEventLoopGroup
- 处理 IO 事件,普通任务,定时任务
- DefaultEventLoopGroup
- 仅处理普通任务与定时任务
2 Channel/Unsafe
Figure 1: Channel 类继承关系
Netty 中的 IO 操作最终都是在 channel pipeline 中由 Unsafe 完成的,pipeline 与 channel 和 usafe 的关系是一对一的,即 pipeline 中包含 channel 引用,channel 中初始化 unsafe 。
3 Future/Promise
io.netty.concurrent.Future
继承自 java.util.concurrent.Future
, io.netty.util.concurrent.Promise
又对 io.netty.concurrent.Future
进行了扩展:
java.util.concurrent.Future
只能同步等待任务结束才能得到结果。
io.netty.concurrent.Future
可以同步等待任务结束从而得到结果【sync()】,也可以异步方式得到结果【addListerner()】,两者都是要以任务结束为前提。Future 一般是由框架代码生成并返回给应用程序使用的。
io.netty.util.concurrent.Promise
可以当做
io.netty.concurrent.Future
使用,也可以作为通用的异步对象使用(即支持主动设置结果),即作为线程间传递结果的载体。 应用程序可以先创建 Promise 对象,再将其交给执行线程(或框架代码)处理。
4 Reactor 线程模型
Figure 2: 宏观模型
Figure 3: Reactor 大致流程
Figure 4: 线程模型
- event loop group 负责给每个 channel 分配 其需要注册的 event loop
- executor 用于创建 一个 内部执行 IO 轮询的线程,线程对象保存在 thread 变量中
- 所有的即时 task 存放在 MPSC 队列中,调度与延时 task 存放在 PQ 中
- 每个 NioEventLoop 代表一个 Reactor 模型
NioEventLoop 初始化过程源码参考
IO 轮询逻辑源码参考
5 IO 模型
5.1 selector
Figure 5: Selector 对象内存使用与 select()
原理
5.2 服务器处理流程
Figure 6: 为了简化逻辑,中间函数调用过程有所忽略,但描述了大致的处理流程
NioSocketChannel 通常只关心 OP_READ 事件,但是如果在写操作时发现暂时无法写入,则会添加 OP_WRITE 感兴趣事件,这样当 selector 轮询到该事件时,说明系统缓冲区空出来了,可以写入了。