在实际生产环境中,高并发、海量连接的情况下,使用一个线程处理所有的请求是不现实的。单个线程不仅资源利用率低,没有充分发挥服务器多核的特性,而且效率不高,无法及时处理请求。
Reactor模式(反应堆模式)
Wikipedia原文如下:“The reactor design pattern is an event handling pattern for handling service requests delivered concurrently by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to associated request handlers.”。
中文解释:Reactor模式首先是:事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handlers;这个Service Handler会同步的将输入的请求(Event)多路复用的分发给相应的Request Handler。
Reactor vs 生产者/消费者模式
该模式类似与生产者与消费者,与生产者与消息者的区别在于:Reactor模式则并没有Queue来做缓冲: 每当一个Event输入到Service Handler之后,该Service Handler会主动的根据不同的Event类型将其分发给对应的Request Handler来处理。
Reactor vs 大文件传输
- Reactor模式在IO读写数据时还是在同一个线程中实现的,即使使用多个Reactor机制的情况下, 那些共享一个Reactor的Channel如果出现一个长时间的数据读写,会影响这个Reactor中其他Channel的相应时间;因此在大文件传输时,IO操作就会影响其他Client的相应时间,因而对这种操作,使用传统的Thread-Per-Connection或许是一个更好的选择,或则此时使用Proactor模式。
Reactor中的三个角色
- Acceptor:用户处理客户端连接请求。Acceptor角色映射到Java代码中,即为SocketServerChannel
- Reactor:用于分派IO就绪事件的处理任务。Reactor角色映射到Java代码中,即为使用多路复用器的Boss线程
- Handler:用于处理具体的IO就绪事件(比如读取并处理数据等)。Handler角色映射到Java代码中,即为Worker线程池中的每个线程
Reactor线程模型
1、Reactor单线程模型
Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下:
- 作为NIO服务端,接收客户端的TCP连接;
- 作为NIO客户端,向服务端发起TCP连接;
- 读取通信对端的请求或者应答消息;
- 向通信对端发送消息请求或者应答消息。
模型与场景分析

适用场景:小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,主要原因如下:
- 1)一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;
- 2)当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;
- 3)可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。
多线程模型
Reactor多线程模型就是将Handler中的IO操作和非IO操作分开,操作IO的线程称为IO线程,非IO操作的线程称为工作线程,这样的话,客户端的请求会直接被丢到线程池中,客户端发送请求就不会堵塞。
多线程模型的特点
- 1) 有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;
- 2) 网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;
- 3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。
适用场景
- 在绝大多数场景下,Reactor多线程模型都可以满足性能需求;
- 但是,当用户进一步增加的时候,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。比如:
- 1) 并发百万客户端连接
- 2) 服务端需要对客户端握手进行安全认证等非常损耗性能的事件。
3. 主从模型
该模型由Doug Lear提出,其设计思路是:将Reactor分为主线程和与子线程。I/O过程依旧是在主线程中的,而服务器实现功能的过程(decode,compute和encode)使用了多线程(子线程),即主线程 负责处理连接请求,子线程负责处理IO事件。
利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。

- 服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。
- Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。
- Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作。
工作流程
- 从主线程池中随机选择一个Reactor线程作为Acceptor线程,用于绑定监听端口,接收客户端连接;
- Acceptor线程接收客户端连接请求之后创建新的SocketChannel,将其注册到主线程池的其它Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操作;
- 步骤2完成之后,业务层的链路正式建立,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,重新注册到Sub线程池的线程上,用于处理I/O的读写操作
原文
网友评论