Reactor 论文简读

论文地址 http://www.dre.vanderbilt.edu/~schmidt/PDF/reactor-siemens.pdf
推荐同时结合 Doug Lea 的 《Scalable IO in Java》 对比查看。

Reactor 组件

重点关注论文中的五种 Reactor 组件:

五种组件之间的关系
五种组件之间的关系

1、Handle(句柄或是描述符)︰本质上表示一种资源,是由操作系统提供的;该资源用于表示一个个的事件,比如说文件描述特,或是针对网络编程中的 Socket 描述符。事件既可以来自于外部,也可以来自于内部;外部事件比如说客户端的连接请求,客户端发送过来数据等;内部事件比如说操作系统产生的定时器事件等。它本质上就是一个文件描述符。Handler 是事件产生的发源地。

2、Synchronous Event Demultiplexer(同步事件分离器):它本身是一个系统调用,用于等待事件的发生(事件可能是一个,也可能是多个),比如说 Socket 的可读事件。调用方在调用它的时候会被阻塞,一直阻塞到同步事件分离器上有事件产生为止。对于 Linux 来说,同步事件分离器指的就是常用的 IO 多路复用机制,比如说 select、 poll、 epoll 等。在 Java NIO 领域中,同步事件分离器对应的组件就是 selector,对应的阻塞方法就是 select 方法。

3、EventHandler(事件处理器)︰本身由多个回调方法构成,这些回调方法构成了与应用相关的对于某个事件的反馈机制。Netty 相比于Java NIO 来说,在事件处理器这个角色上进行了一个升级,它为我们开发者提供了大量的回调方法,供我们在特定事件产生时实现相应的回调方法进行业务逻辑的处理。

4、Concrete Bvent Handler(具体事件处理器)∶是事件处理器的实现。它本身实现了事件处理器所提供的各个回调方法,从而实现了特定于业务的逻辑。它本质就是我们所编写的一个个的处理器实现。比如说我们在使用 Netty 时实现的 SimpleChannelInboundHandler#channelRead0 ,channelRead0 方法体就是一个事件处理器。

5、Initiation Dispatcher (初始分发器)︰实际上就是 Reactor 角色。它本身定义了一些规范,这些规范用于控制事件的调度方式。同时又提供了应用进行事件处理器的注册、删除等机制。它本身是整个事件处理器的核心所在,Initiation Dispatcher 会通过同步事件分离器来等待事件的发生。一旦事件发生,Initiation Dispatcher 首先会分离出每一个事件,然后调用事件处理器,最后调用相关的回调方法来处理这些事件。

Reactor 流程

1、当应用向 Initiation Dispatcher 注册具体的事件处理器的时候,应用会标志出该事件处理器希望Initiation Dispatcher 在某个事件发生时向其通知的该事件,该事件与 handle 相关联。(就是注册其感兴趣的事件)

2、Initiation Dispatcher 会要求每个事件向其传递内部的 handle,该 handle 向操作系统标志了事件处理器。(就是每个事件处理器与一个handle相关联)。

3、当所有的事件处理器注册完成以后,应用会调用 handle_event 方法来启动 Initiation Dispatcher 事件循环。这时,Initiation Dispatcher 会 将每个注册事件管理器的 handle 合并起来,并且使用同步事件分离器等待这些事件的发生。比如说 TCP 协议层会使用 select 同步事件分离器操作来等待客户端发送的数据到达连接的 socket handle 上。

4、当与某个事件源对应的 handle 变成 ready 状态的时候(比如 TCP/socket 变成等待读的状态),同步事件分离器就会通知 Initiation Dispatcher。

5、Initiation Dispatcher 会触发事件处理器的回调方法,从而响应这个处于ready状态的 handle。当事件发生时,Initiation Dispatcher 会将被事件源激活的 handle 作为 key 来寻找并分发恰当的事件处理器回调方法。

6、Initiation Dispatcher 会回调事件处理器的 handle_events 回调方法来执行特定于应用的功能(开发者自己开发的),从而响应这个事件。所发生的事件类型可以作为该方法的参数并被该方法内部使用来执行额外的特定与服务的分离与分发。

根据上面提供的 Reactor 模型可以总结出 Reactor 的两个重要特征:分而治之、事件驱动

基于事件驱动的架构设计通常比其他架构模型更加有效,因为可以节省一定的性能资源,事件驱动模式下通常不需要为每一个客户端建立一个线程,这意味这更少的线程开销,更少的上下文切换和更少的锁互斥,但任务的调度可能会慢一些,而且通常实现的复杂度也会增加,相关功能必须分解成简单的非阻塞操作,类似与GUI的事件驱动机制,当然也不可能把所有阻塞都消除掉,特别是GC, page faults(内存缺页中断)等。由于是基于事件驱动的,所以需要跟踪服务的相关状态(因为你需要知道什么时候事件会发生); 源自:https://www.codenong.com/cs107103129/

Reactor 模型实践

使用多 Reactor 模型构建程序,模型图如下:

Mutliple Reactors
Mutliple Reactors

拆分并增加反应器 Reactor 线程,一方面在压力较大时可以饱和处理 IO 操作,提高处理能力;另一方面维持多个 Reactor 线程也可以做负载均衡使用;线程的数量可以根据程序本身是 CPU 密集型还是 IO 密集型操作来进行合理的分配。