1. 基本概念
1.1 背景
为了处理Web请求,有以下两种架构处理方式
1.1.1 基于线程的架构
也就是一个连接一个线程,即BIO的方式
优点:
可以隔离每个请求,请求之间互不影响
缺点:
严重依赖于线程,虽然线程相比于进程轻量级,但还是很占用资源
线程的创建和销毁成本很高(可以通过线程池和有界阻塞队列优化)
线程本身占用较大的内存
线程的切换成本是很高的
容易造成锯齿状的系统负载
无法解决C10K问题
适用场景
适用于连接数目比较小且固定的架构
1.1.2 事件驱动架构
通过事件驱动的方法可以将线程与连接分开,连接只将线程用于特定回调或程序的事件处理。
Reactor是事件驱动架构的一种实现技术。
简单来说,它使用单线程事件循环对资源发出的事件进行阻塞,并将其分配给相应的处理程序和回调。
只要注册了事件的处理程序和回调来处理它们,就不需要阻塞I O。事件是指实例,例如新的传入连接,可以读取,可以写入等。这些处理程序/回调可以在多核环境中利用线程池。
优点
事件处理器之间高度解耦,扩展性好
性能较好,因为事件的异步本质,不易产生阻塞
缺点
开发相对复杂
1.2 Reactor的概念
1.2.1 Reactor模式的组成部分:
Reactor
负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理;
对应的实现有Java的Selector
Handler
处理程序执行与I O事件有关的实际工作。
1.2.2 优点
响应快,不必为单个事件处理所阻塞,虽然Reactor本身依然是同步的;
编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;
可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;
可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;
1.2.3 缺点
相比传统的简单模型,Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试。
Reactor模式需要底层的Synchronous Event Demultiplexer支持
比如Java中的Selector支持,操作系统的select系统调用支持,如果要自己实现Synchronous Event Demultiplexer可能不会有那么高效。
不适用于类似于大文件传输等耗时长的IO操作
Reactor模式在IO读写数据时还是在同一个线程中实现的,即使使用多个Reactor机制的情况下,那些共享一个Reactor的Channel如果出现一个长时间的数据读写,会影响这个Reactor中其他Channel的相应时间
使用传统的Thread-Per-Connection或许是一个更好的选择,或者此时使用改进版的Reactor模式如Proactor模式。
2. 单线程Reactor模型
Reactor模型的朴素原型:所有的操作都在同一个NIO线程上面完成
2.1 单线程Reactor的介绍
如下图所示:
这是最简单的单Reactor单线程模型。Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到Handler处理器中。
Reactor和Hander 处于一条线程执行。

2.2 Reactor单线程模式的分析
由于是单线程,只要有一个handler出现阻塞情况,会导致其他client都被阻塞。并且不能充分利用多核资源
因此,单线程模型仅仅适用于handler 中业务处理组件能快速完成的场景。
2.3 单线程Reactor的参考代码
基于Java的NIO基础实现的单线程Reactor,可以参考:https://www.icode9.com/content-4-622761.html
基于Netty实现的单线程Reactor模型代码如下:
最核心的方法:ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup)
parentGroup主要是用来接收socket连接请求,然后会产生一个channel,并把这个channel交给childGroup线程来处理
childGroup主要用来处理channel里的所有事件和IO
public void bind(int port) {EventLoopGroup reactorGroup = new NioEventLoopGroup(1);try {ServerBootstrap b = new ServerBootstrap();b.group(reactorGroup, reactorGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast("http-codec", new HttpServerCodec());ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());后面代码省略}});Channel ch = b.bind(port).sync().channel();ch.closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {reactorGroup.shutdownGracefully();}}
3. 多线程的Reactor
3.1 基于线程池的改进
在单线程Reactor模式基础上,做如下改进:
将Handler处理器的执行放入线程池,多线程进行业务处理。
而对于Reactor而言,可以仍为单个线程
一个简单的图如下:

3.2 特点
有一个专门的NIO线程用于监听服务端,接收客户端的TCP连接请求;
网络I/O操作--读、写等以及业务处理由一个NIO线程池负责,由这些NIO线程负责消息的读取、解码、编码和发送;
3.3 适用场景
在绝大多数场景下,Reactor多线程模型都可以满足性能需求;
但是,在极特殊应用场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如百万客户端并发连接,或者服务端需要对客户端的握手信息进行安全认证,认证本身非常损耗性能。这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型--主从Reactor多线程模型。
3.4 多线程Reactor的参考代码
基于Java的NIO的多线程Reactor的代码实现可以参考:https://www.cnblogs.com/crazymakercircle/p/9833847.html
基于Netty的多线程Reactor的代码实现
与单线程Reactor的Netty实现不同的是没有共用同一个EventLoopGroup
public void bind2(int port) {EventLoopGroup acceptorGroup = new NioEventLoopGroup(1);NioEventLoopGroup ioGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(acceptorGroup, ioGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast("http-codec", new HttpServerCodec());ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());后面代码省略}});Channel ch = b.bind(port).sync().channel();ch.closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {acceptorGroup.shutdownGracefully();ioGroup.shutdownGracefully();}}
4. Reactor持续改进
4.1 主从Reactor多线程模型的特点
在多线程Reactor的基础上,将接收客户端连接的部分(acceptor部分)使用线程池来处理
acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到I/O线程池(sub reactor线程池)的某个I/O线程上,由它负责SocketChannel的读写和编解码以及业务处理工作。
4.2 主从Reactor的参考代码
基于Java的NIO的主从Reactor代码实现可以参考:https://blog.csdn.net/weixin_42412601/article/details/106610642
基于Netty的主从Reactor代码实现
public void bind3(int port) {EventLoopGroup acceptorGroup = new NioEventLoopGroup();NioEventLoopGroup ioGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(acceptorGroup, ioGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast("http-codec", new HttpServerCodec());ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());//后面代码省略}});Channel ch = b.bind(port).sync().channel();ch.closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {acceptorGroup.shutdownGracefully();ioGroup.shutdownGracefully();}}
5. 参考文章
https://www.cnblogs.com/crazymakercircle/p/9833847.html
https://dzone.com/articles/understanding-reactor-pattern-thread-based-and-eve
https://tech.meituan.com/2016/11/04/nio.html
https://blog.csdn.net/woaixiaopangniu521/article/details/70256018
https://www.cnblogs.com/duanxz/p/3696849.html




