Reactor设计模式
Reactor模式称为反应器模式或应答者模式,是基于事件驱动的设计模式,拥有一个或多个并发输入源,有一个服务处理器和多个请求处理器,服务处理器会同步的将输入的请求事件以多路复用的方式分发给相应的请求处理器。
Reactor设计模式是一种为处理并发服务请求,并将请求提交到一个或多个服务处理程序的事件设计模式。当客户端请求抵达后,服务处理程序使用多路分配策略,由一个非阻塞的线程来接收所有请求,然后将请求派发到相关的工作线程并进行处理的过程。
在事件驱动的应用中,将一个或多个客户端的请求分离和调度给应用程序,同步有序地接收并处理多个服务请求。对于高并发系统经常会使用到Reactor模式,用来替代常用的多线程处理方式以节省系统资源并提高系统的吞吐量。
Reactor结构
- Handle(事件)。Reactor整体是基于Handle进行驱动,这里的Handle叫做事件,可以类比为BIO中的Socket,NIO中的Socket管道。比如当Socket管道有连接建立,或者有数据可读,那么此时就称作事件发生;
- Event Handler(事件处理器)。有事件发生,就需要有相应的组件来处理事件,那么这里的组件就叫做事件处理器。Event Handler是一个抽象概念,其会有不同的具体实现,因为事件会有不同的类型,那么不同类型的事件,肯定都需要有相应的具体处理逻辑,这里的具体处理逻辑,就由Event Handler的具体实现来承载;
- Concrete Event Handler(具体事件处理器)。是Event Handler的具体实现,用于处理不同类型的事件;
- Synchronous Event Demultiplexer(事件多路分解器)。(这里将Synchronous Event Demultiplexer简称为Demultiplexer)Demultiplexer用于监听事件并得到所有发生事件的集合,在监听的状态下是阻塞的,直到有事件发生为止。Demultiplexer有一个很好的类比,就是NIO中的多路复用器Selector,当调用Selector的select() 方法后,会进入监听状态,当从select() 方法返回时,会得到SelectionKey的一个集合,而每一个SelectionKey中就保存着有事件发生的Socket管道;
- Initiation Dispatcher(事件分发器)。现在已经有Concrete Event Handler(具体事件处理器)来处理不同的事件,也能通过Synchronous Event Demultiplexer(事件多路分解器)拿到发生的事件,那么最后需要做的事情,肯定就是将事件分发到正确的事件处理器上进行处理,而Initiation Dispatcher就是完成这个分发的事情。
通常,Reactor设计模式中的Reactor,可以理解为上述图中的Synchronous Event Demultiplexer + Initiation Dispatcher。
Reactor中的核心组件:
- Reactor
IO事件的派发者,相当于有分发功能的Selector
。 - Acceptor
接收客户端连接并建立对应客户端的Handler
,向Reactor注册此Handler
。相当于NIO中建立连接的那个判断分支。 - Handler
和一个客户端通讯的实体,一般在基础的Handler
上会有更进一步的层次划分,用来抽象诸如decode
、process
、encode
这些过程。相当于消息读写处理等操作类。
在Reactor模式中,acceptor和handler都属于事件处理器。不过,它们处理的事件类型和职责有所不同:
- Acceptor :是一种特殊的事件处理器,专门负责处理新的连接请求。当Reactor将新的连接请求事件分发给acceptor时,acceptor会调用底层系统的
accept
函数来接受这个连接,然后通常会为这个新的连接创建一个新的handler来处理后续的事件(比如数据读写)。 - Handler:通常指的是连接建立之后用于处理特定事件的组件,如读取客户端发送的数据、处理业务逻辑、向客户端发送响应等。Handler响应其他类型的事件,如可读事件、可写事件等。
Reactor三种模型
Reactor从线程池和Reactor的选择上可细分为:Reactor单线程模型、Reactor多线程模型,Reactor主从模型
Reactor单线程模型
单线程的Reactor模式对于客户端的所有请求使用一个专门的线程去处理,这个线程无限循环地监听是否有客户端的请求抵达,一旦收到客户端的请求,就将其分发给响应处理程序进行处理。
单线程的Reactor与NIO流程类似,只是将消息相关处理独立到Handler
中。虽然NIO中一个线程可以支持所有的IO处理,但瓶颈也是显而易见的。如果某个客户端多次进行请求时在Handler
中的处理速度较慢,那么后续的客户端请求都会被积压,导致响应变慢。所以需要引入Reactor多线程模型。
单线程的Reactor的特点是只有一个Reactor线程,也就是说只有一个Selector
事件通知器,因此字节的读取I/O和后续的业务处理process()
均由Reactor线程来做,很显然业务的处理影响后续事件的分发,所以引出多线程版本进行优化。
Reactor多线程模型
考虑到工作线程的复用,可以将工作线程设计线程池。将处理器的执行放入线程池,并使用多线程处理业务逻辑,Reactor仍然是单个线程。
多线程的Reactor的特点是一个Reactor线程和多个处理线程,将业务处理即process交给线程池进行了分离,Reactor线程只关注事件分发和字节的发送和读取。需要注意的是,实际的发送和读取还是由Reactor来处理。当在高并发环境下,有可能会出现连接来不及接收。
Reactor主从模型
对于多个CPU的机器,为了充分利用系统资源会将Reactor拆分为两部分。
- Main Reactor 负责监听连接,将
accept
连接交给Sub Reactor
处理,主Reactor用于响应连接请求。 - Sub Reactor 处理
accept
连接,从Reactor用于处理IO操作请求。
主从Reactor的特点是使用 一个Selector
池,通常有一个主Reactor
用于处理接收连接事件,多个从Reactor
处理实际的IO。整体来看,分工合作,分而治之,非常高效。