总体介绍

将关注的 I/O 事件注册到多路复用器上,一旦有 I/O 事件触发,将事件分发到事件处理器中,执行就绪 I/O 事件对应的处理函数中。模型中有三个重要的组件:

  • 多路复用器:由操作系统提供接口,Linux 提供的 I/O 复用接口有select、poll、epoll 。
  • 事件分离器:将多路复用器返回的就绪事件分发到事件处理器中。
  • 事件处理器:处理就绪事件处理函数。

reactor模型是同步I/O事件处理的一种常见模型,关于Reactor模型结构的资料非常多,一个典型的Reactor模型类图结构如图所示,



                   图 Reactor 模型类结构图

图所示,Reactor 类结构中包含有如下角色。

Handle:标示文件描述符;
Event Demultiplexer:执行多路事件分解操作,对操作系统内核实现I/O复用接口的封装;用于阻塞等待发生在句柄集合上的一个或多个事件(如select/poll/epoll);
Event Handler:事件处理接口;
Event Handler A(B):实现应用程序所提供的特定事件处理逻辑;
Reactor:反应器,定义一个接口,实现以下功能:
a)供应用程序注册和删除关注的事件句柄;
b)运行事件处理循环;
c)等待的就绪事件触发,分发事件到之前注册的回调函数上处理.

Reactor3种模型实现

  • 单 Reactor 单线程模型
  • 单 Reactor 多线程模型
  • 多 Reactor 多线程模型

netty客户端

  • 对于 Netty NIO 客户端来说,仅创建一个 EventLoopGroup 。
  • 一个 EventLoop 可以对应一个 Reactor 。因为 EventLoopGroup 是 EventLoop 的分组,所以对等理解,EventLoopGroup 是一种 Reactor 的分组。
  • 一个 Bootstrap 的启动,只能发起对一个远程的地址。所以只会使用一个 NIO Selector ,也就是说仅使用一个 Reactor 。即使,我们在声明使用一个 EventLoopGroup ,该 EventLoopGroup 也只会分配一个 EventLoop 对 IO 事件进行处理。
    因为 Reactor 模型主要使用服务端的开发中,如果套用在 Netty NIO 客户端中,到底使用了哪一种模式呢?
    如果只有一个业务线程使用 Netty NIO 客户端,那么可以认为是【单 Reactor 单线程模型】。
    如果有多个业务线程使用 Netty NIO 客户端,那么可以认为是【单 Reactor 多线程模型】。
    那么 Netty NIO 客户端是否能够使用【多 Reactor 多线程模型】呢?创建多个 Netty NIO 客户端,连接同一个服务端。那么多个 Netty 客户端就可以认为符合多 Reactor 多线程模型了。
    一般情况下,我们不会这么干。
  • 当然,实际也有这样的示例。例如 Dubbo 或 Motan 这两个 RPC 框架,支持通过配置,同一个 Consumer 对同一个 Provider 实例同时建立多个客户端连接。

netty 服务端

  • 对于 Netty NIO 服务端来说,创建两个 EventLoopGroup 。
    bossGroup 对应 Reactor 模式的 mainReactor ,用于服务端接受客户端的连接。比较特殊的是,传入了方法参数 nThreads = 1 ,表示只使用一个 EventLoop ,即只使用一个 Reactor 。这个也符合我们上面提到的,“通常,mainReactor 只需要一个,因为它一个线程就可以处理”。
  • workerGroup 对应 Reactor 模式的 subReactor ,用于进行 SocketChannel 的数据读写。对于 EventLoopGroup ,如果未传递方法参数 nThreads ,表示使用 CPU 个数 Reactor 。这个也符合我们上面提到的,“通常,subReactor 的个数和 CPU 个数相等,每个 subReactor 独占一个线程来处理”。
    因为使用两个 EventLoopGroup ,所以符合【多 Reactor 多线程模型】的多 Reactor 的要求。实际在使用时,workerGroup 在读完数据时,具体的业务逻辑处理,我们会提交到专门的业务逻辑线程池,例如在 Dubbo 或 Motan 这两个 RPC 框架中。这样一来,就完全符合【多 Reactor 多线程模型】。
  • bossGroup 如果配置多个线程,是否可以使用多个 mainReactor 呢?我们来分析一波,一个 Netty NIO 服务端同一时间,只能 bind 一个端口,那么只能使用一个 Selector 处理客户端连接事件。又因为,Selector 操作是非线程安全的,所以无法在多个 EventLoop ( 多个线程 )中,同时操作。所以这样就导致,即使 bossGroup 配置多个线程,实际能够使用的也就是一个线程。
    那么如果一定一定一定要多个 mainReactor 呢?创建多个 Netty NIO 服务端,并绑定多个端口。