在网络IO设计中,有两种高性能模型:Reactor模型和Proactor模型。
Reactor基于同步IO模式,Proactor基于异步IO模式。
不过,无论是 Reactor,还是 Proactor,都是一种基于「事件分发」的网络编程模式,区别在于 Reactor 模式是基于「待完成」的 I/O 事件,而 Proactor 模式则是基于「已完成」的 I/O 事件。
一、Reactor
Reactor模型主要就是监听事件,分发事件和处理事件。其中Reactor角色会负责监听事件 和分发事件,Handler角色和Acceptor角色会负责处理事件。
Netty网络框架,Redis等中间件中都有使用到Reactor模型。
1.单 Reactor 单线程(Redis使用)
Java 语言实现的是「单 Reactor 单线程」的方案,因为 Java 程序是跑在 Java 虚拟机这个进程上面的,虚拟机中有很多线程,我们写的 Java 程序只是其中的一个线程而已。
优点
- 将所有处理逻辑放在一个线程中实现,没有多线程通信、竞争的问题。
缺点
- 一个 Reactor 既要负责处理连接请求,又要负责处理读写请求,在面对瞬间高并发的场景时,容易成为性能的瓶颈的地方。
- 一旦 Reactor 线程意外中断或者进入死循环,会导致整个系统通信模块不可用。
- 只有一个线程在工作,处理效率低,无法利用多核CPU的优势。
- Handler 对象在业务处理时,整个线程是无法处理其他连接的事件的。
2.单 Reactor 多线程
优点
- 多线程模式可以充分利用 多核CPU 的性能。
缺点
- 单 Reactor 存在两个的问题(同上)。
- Handler 使用多线程模式,自然带来了多线程竞争资源的开销,同时涉及共享数据的互斥和保护机制,实现比较复杂。(例如,子线程完成业务处理后,要把结果传递给主线程的 Handler 进行发送,这里涉及共享数据的竞争)
3.主从 Reactor 多线程(Netty使用)
思考:为什么一个Reactor同时监听连接事件和处理读写事件是不好的?
- 一般来说处理连接请求是很快的,但处理读写请求时涉及到业务逻辑处理,相对慢很多。所以 Reactor 在处理读写请求时,其他请求只能等着,容易造成系统的性能瓶颈。
- 客户端连接的建立是不频繁的,但是连接建立后数据的收发是频繁的,所以如果能够将处理读写事件这个动作拆分出来,让多个子Reactor来处理读写事件,而原来的主Reactor只监听连接事件,那么整体的效率,会进一步提升,而这,就是主从Reactor多线程模型。
主线程中的 MainReactor 对象通过 select 监控连接建立事件,收到事件后通过 Acceptor 对象中的 accept 获取连接,将新的连接分配给某个子线程;
子线程中的 SubReactor 对象将 MainReactor 对象分配的连接加入 select 继续进行监听,并创建一个 Handler 用于处理连接的响应事件。
优点
- 主线程和子线程分工明确,主线程只负责接收新连接,子线程负责完成后续的业务处理。
- 主线程和子线程的交互也很简单,子线程接收主线程的连接后,只管业务处理即可,无须关注主线程,可以直接在子线程将处理结果发送给客户端。
- 适用于高并发场景。
二、Proactor
关于同步与异步的概念,大家可以看这篇:
现在我们再来理解 Reactor 和 Proactor 的区别,就比较清晰了。
- Reactor 是非阻塞同步网络模式,感知的是就绪可读写事件。在每次感知到有事件发生(比如可读就绪事件)后,就需要应用进程主动调用 read 方法来完成数据的读取,也就是要应用进程主动将 socket 接收缓存中的数据读到应用进程内存中,这个过程是同步的,读取完数据后应用进程才能处理数据。
- Proactor 是异步网络模式, 感知的是已完成的读写事件。在发起异步读写请求时,需要传入数据缓冲区的地址(用来存放结果数据)等信息,这样系统内核才可以自动帮我们把数据的读写工作完成,这里的读写工作全程由操作系统来做,并不需要像 Reactor 那样还需要应用进程主动发起 read/write 来读写数据,操作系统完成读写工作后,就会通知应用进程直接处理数据。
因此,Reactor 可以理解为「来了事件操作系统通知应用进程,让应用进程来处理」,而 Proactor 可以理解为「来了事件操作系统来处理,处理完再通知应用进程」。这里的「事件」就是有新连接、有数据可读、有数据可写的这些 I/O 事件这里的「处理」包含从驱动读取到内核以及从内核读取到用户空间。
无论是 Reactor,还是 Proactor,都是一种基于「事件分发」的网络编程模式,区别在于 Reactor 模式是基于「待完成」的 I/O 事件,而 Proactor 模式则是基于「已完成」的 I/O 事件。
介绍一下 Proactor 模式的工作流程:
- Proactor Initiator 负责创建 Proactor 和 Handler 对象,并将 Proactor 和 Handler 都通过 Asynchronous Operation Processor 注册到内核;
- Asynchronous Operation Processor 负责处理注册请求,并处理 I/O 操作;
- Asynchronous Operation Processor 完成 I/O 操作后通知 Proactor;
- Proactor 根据不同的事件类型回调不同的 Handler 进行业务处理;
- Handler 完成业务处理;
三、参考
9.3 高性能网络模式:Reactor 和 Proactor | 小林coding (xiaolincoding.com)