Reactor模型是服务器端用于处理高并发网络IO请求的编程模型,与传统的一请求一线程的同步式编程模型不同的是,Reactor模型是基于事件驱动的响应式编程模型,可以一个线程处理多个请求,并且是异步处理,在高并发场景下,性能大大的提升。
传统的同步式编程模型如下:

服务端接收到请求后,从线程池中拿出一个线程(比如Tomcat里的线程池),经过Controller、Service、Dao的一顿处理,最后返回响应结果给客户端。这种同步处理请求的方式效率并不高,可以应对一些并发量不高的场景。
基于事件驱动的响应式编程模型如下:

Reactor其实就是一个线程,可以看到它就在一个死循环中进行事件监听以及事件分派,这就是事件循环。
Reactor模型底层使用了IO多路复用,通过对IO多路复用机制的合理利用,使得服务器端达到高效的处理网络IO请求的目的。我们可以使用Java提供的NIO来实现Java版本的Reactor模型。

Reactor模型抽象了三类事件和三类角色,它们是Reactor模型的核心组成部分。三类事件是:连接就绪事件,读就绪事件、写就绪事件。三类角色是:Reactor、Acceptor、Handler。
首先我们来了解一下三类事件。

我们再来了解一下三类角色。

Reactor是相当于是一个事件分派器,Reactor专门负责监听事件的发生,并把发生的事件交给对应的处理器处理,这里的处理器指的就是Acceptor或Handler。Reactor是一个线程,它通过Selector注册并监听多个Channel,如果监听到有事件发生,判断是连接就绪事件,会交给Acceptor处理,如果发生的事件是读就绪事件或写就绪事件,会交给Handler处理。

如果是连接就绪事件,Reactor会将其分派给Acceptor处理,Acceptor会调用accept()方法获取到连接对应的SocketChannel,然后会将其设置为非阻塞,注册到Reactor的Selector中,设置关注的事件为读就绪事件。

如果是读就绪事件,那么Reactor会将其分派给与其绑定的Handler处理,该Handler会经过read、decode、compute、encode、send五个步骤的处理。
之所以要进行decode和encode,是因为数据在网络上是以二进制的形式传输的,因此服务端接收到后要对二进制字节码进行解码操作,然后才能对解码后的数据进行处理,处理完的数据要发送到网络,也要编码成二进制的格式。

其中,send操作可以立刻发送数据写出到Channel,也可以注册一个写就绪事件到Selector,这就相当于进行异步发送。


Reactor编程模型不是只有单一的一种编程模型,它有单Reactor单线程模型、单Reactor多线程模型、主从Reactor模型等多种模型。
单Reactor单线程模型是最简单的模型,当然性能也是最低的。单Reactor模型只有一个Reactor,这个Reactor即负责处理连接建立,也负责数据读写和计算等处理。并且由于是单线程模型,所以数据读取后的计算处理,也是在当前线程中完成。

这种单Reactor单线程模型虽然性能比其他的Reactor模型低,但是优点是比较的简单,没有了多线程就意味着没有了“多线程翻车”的一些情况出现。值得一提的是,Redis的事件驱动框架就是单Reactor单线程模型。
但是由于连接建立与IO处理都在一个Reactor线程中进行,因此在高并发场景下,该Reactor线程的压力会非常大,特别是在数据计算还比较复杂的场景,单线程的Reactor模型有可能支撑不住。之所以Redis使用单Reactor单线程模型还能支撑高并发读写的场景,其中一个原因是因为Redis内部不涉及太复杂的数据计算。
单Reactor多线程模型是在单Reactor模型的基础上增加了线程池,可以把decode、compute、encode等需要计算的操作提交到线程池执行,而Reactor线程则专注于连接建立与IO的处理(也就是read和send)。

把涉及到计算的处理抽到了线程池中处理,可以在一定程度上缓解Reactor线程的压力。但是由于还是单Reactor模型,IO处理依然在该Reactor线程中进行,高并发场景下,可能会由于大量的IO请求需要处理而导致连接建立的处理受到影响,该Reactor线程可能无法及时响应客户端连接建立的请求。
主从Reactor模型在单Reactor多线程模型基础上做了改进,把IO处理分离到了子Reactor中,主Reactor只专注于连接请求的处理,主Reactor一般只有一个(也可有多个),而子Reactor一般有多个。当主Reactor上有连接就绪事件发生时,通过Acceptor获取到连接对应的SocketChannel,然后把该SocketChannel注册到子Reactor中,由子Reactor监听该Channel的读就绪事件并处理。

由于连接建立与IO处理分离到了不同的Reactor,当有大量并发的IO请求需要处理时,也不会影响到客户端建立连接的请求。