这一节和我一起开始正式的去研究 Netty 源码. 在研究之前, 我想先介绍一下 Reactor 模型.
我先分享两篇文献, 大家可以自行下载学习. 链接: https://pan.baidu.com/s/1Utym7AS8DHCxrh5aNOD2Ew 提取码: 18u3
下面主要是对文献中一些内容和重要的图片进行说明:
1,Each handler may be started in its own thread, 这种方式在客户端负载增加的时候, 有很严重的性能问题.
serversocket 的 accept 方法, 阻塞等待 client 连接, 直到 client 连接成功.
线程从 socket inputstream outputstream 读写数据, 会进入阻塞状态, 直到全部数据读写完.
即使没有数据的读写, 仍然会占用一个线程, 浪费资源.
2, 对上面的模型进行优化, 采用事件驱动的模式, 有事件发生的时候才会触发:
Reactor 为单个线程, 既要处理客户端的 accept 事件, 又要负责发送请求到处理器中. 由于只有单个线程, 所以处理器中的业务需要能够快速处理完.
3, 再次进行改进, 加入线程池
这样 Reactor 线程处理完连接之后, 将请求放在线程池中进行处理, 不会受到 Handler 的阻塞.
4, 使用多个 Reactor
mainReactor 负责监听连接, accept 连接给 subReactor 处理, 为什么要单独分一个 Reactor 来处理监听呢? 因为像 TCP 这样需要经过 3 次握手才能建立连接, 这个建立连接的过程也是要耗时间和资源的, 单独分一个 Reactor 来处理, 可以提高性能. mainReactor 仅仅只负责处理客户端连接, subReactor 负责后面的数据的读写.
在解决了什么是 Reactor 模式后, 我们来看看 Reactor 模式是由什么模块构成. 图是一种比较简洁形象的表现方式, 因而先上一张图来表达各个模块的名称和他们之间的关系:
上面的图由五大角色构成, 下面进行解释:
Handle(句柄或描述符): 本质上是一种资源, 由操作系统提供的, 该资源用于表示一个个的事件, 比如说文件描述符, 或是针对网络编程中的 Socket 描述符, 事件既可以来自于外部, 又可以来自于内部, 外部事件比如客户端的连接请求, 客户端数据的读写等等; 内部事件比如操作系统产生的定时器事件, 它本质上就是一个文件描述符. 简单来说, Handle 就是事件产生的发源地.
Synchronous Event Demultiplexer(同步事件分离器): 它本身是一个系统调用, 用于等待事件的发生 (一个或多个). 调用方在调用它的时候会一直阻塞, 一直阻塞到同步分离器上有事件产生, 对于 Linux 来说, 同步事件分离器指的就是常用的 I/O 多路复用器, 比如说 select,poll,epoll 等, 在 Java NIO 领域中, 同步事件分离器的组件就是 Selector; 对应的阻塞方法就是 select()
Event Hander (事件处理器): 本身由多个回调方法组成, 这些回调方法构成了于应用相关的对于某个事件的反馈机制, Netty 相比于 Java 的 NIO 来说, 在事件处理器的这个角色上进行了一个升级, 它为我们开发者指定了大量的回调方法, 供我们在特定时间产生的时候实现相应的回调方法进行业务逻辑的处理.
Concrete Event Handler(具体事件处理器): 是事件处理器的时间, 本质上是我们所编写的一个个的处理器的实现
Initiation Dispatcher (初始分发器): 实际上就是 Reactor 的角色, 它本身定义了一些规范, 这些规范用户控制事件的调度方式, 同时又提供了应用事件处理器的注册, 删除等设施, 它本身是整个事件处理器的核心所在, 会通过同步事件分离器来等待事件的发生, 一旦事件发生, 首先会分离出一个事件, 然后调用事件处理器, 最后调用相关的回调方法来处理这些事件.
来源: https://www.cnblogs.com/huxipeng/p/10733563.html