文章目录
- 一. 什么是Reactor模式
- 二. Reactor模式实现种类
- 1.单Reactor单线程模式
- 2.单Reactor多线程模式:
- 3.主从Reactor多线程
在正式进入Netty编程模型之前,还需要了解Reactor模式,因为netty就是基于Reactor模式做了一定的改进形成的netty模型。
一. 什么是Reactor模式
首先,Reactor是一种设计模式,不是具体的类,上篇文章中《Netty系列(一)java NIO详解及使用 Demo》,简单介绍了java NIO相关内容,其中的demo就是Reactor模式的一种最简单的实现。
实际上的Reactor模式,是基于Java NIO的,在他的基础上,抽象出来两个组件——Reactor和Handler两个组件:
- Reactor:负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理;新的事件包含连接建立就绪、读就绪、写就绪等。
- Handler:将自身(handler)与事件绑定,负责事件的处理,完成channel的读入,完成处理业务逻辑后,负责将结果写出channel。
如果要自己实现Reactor模式,其中的Reactor和Handler都是需要我们自定义的。
Reactor与Selector的关系?
它俩不是一个层面的东西,Reactor是一种模型,算是设计模式,也就是一种概念;Selector是java NIO包中的一个具体的类,实现Reactor模型,要用到selector这个类。真正实现的时候,我们还会自己创建一个Reactor(名字随意)类,这个类里面包含使用Selector工具类。
二. Reactor模式实现种类
Reactor大致有3种实现方式,每种都有它针对的使用场景,具体可以参考大佬的文档《Scalable IO in Java》
1.单Reactor单线程模式
或者看Scalable IO in Java中的图:
以上两个图是一个意思。如果熟悉NIO,上图很容易理解,不熟悉的话建议先看下上篇文章。
方案说明:
- Select 是前面 I/O 复用模型介绍的标准网络编程 API,可以实现应用程序通过一个阻塞对象监听多路连接请求;
- Reactor 对象,是我们自定义的,通过 Selector 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;Dispatch我们一般会抽象出来一个方法或者一个类对象。
- 如果是建立连接请求事件,则由 Acceptor(自定义类对象)通过处理连接请求,然后创建一个 Handler(自定义对象)对象处理连接完成后的后续业务处理,比如read、decode、compute、encode、send;
- 如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;
- Handler 会完成 Read→业务处理→Send 的完整业务流程。
优缺点
- 优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成;
- 缺点: 性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈;
- 缺点:可靠性问题,线程意外终止,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。
使用场景
客户端的数量有限,业务处理非常快速,比如 Redis在业务处理的时间复杂度 O(1) 的情况。
2.单Reactor多线程模式:
或者看Scalable IO in Java中的图:
以上两个图是一个意思。
方案说明:
与单线程版本相比,变化的地方是Handler部分,Handler的职责减轻了,
- handler 只负责响应事件,不做具体的业务处理, 通过read 读取数据后,会分发给后面的worker线程池的某个线程处理业务;
- worker 线程池会分配独立线程完成真正的业务,并将结果返回给handler;
- handler收到响应后,通过send 将结果返回给client。
优点: 可以充分的利用多核cpu 的处理能力。
缺点: 多线程数据共享和访问比较复杂,reactor 部分,使用单线程处理所有的事件的监听和响应,在高并发场景容易出现性能瓶颈。
3.主从Reactor多线程
或者看Scalable IO in Java中的图:
以上两个图是一个意思。
方案说明:
- Reactor主线程 MainReactor 对象通过select 监听连接事件, 收到事件后,通过Acceptor 处理连接事件;
- 当 Acceptor 处理连接事件后,MainReactor 将连接分配给SubReactor ;
- subreactor 将连接加入到连接队列进行监听,并创建handler进行各种事件处理;
- 当有新事件发生时, subreactor 就会调用对应的handler处理;
- handler 通过read 读取数据,分发给后面的worker 线程处理;
- worker 线程池分配独立的worker 线程进行业务处理,并返回结果;
- handler 收到响应的结果后,再通过send 将结果返回给client;
- Reactor 主线程可以对应多个Reactor 子线程, 即MainRecator 可以关联多个SubReactor。
优缺点:
优点:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。
优点:父线程与子线程的数据交互简单,Reactor 主线程只需要把新连接传给子线程,子线程无需返回数据。
缺点:编程复杂度较高
Netty就是基于主从reactor多线程模型改进来的。这种模型在许多项目中广泛使用,包括 Nginx 主从 Reactor 多进程模型,Memcached 主从多线程。