介绍reactor和模拟proactor两种高性能网络模式。
reactor翻译过来是反应堆,reactor模式也叫dispatch模式。
本文图片来自小林coding。
单reactor单线程模式

单reactor模式就是一份之前写过的代码,只是在功能上将代码划分为Reactor、Acceptor和Handler三个部分,但是全部交给在一个进程/线程完成。其中Reactor进行请求连接、读写时间的侦听,并将这些事件按照类别进行分发。如果是建立连接事件,那么单独使用Acceptor来处理这部分逻辑,如果是处理读写请求事件,那么便调用相应的handler函数。
这个模式只有单个线程,只是在代码上进行了功能划分,使用简单,一个典型的应用是redis(redis由于只和CPU和内存打交道,使用单线程单reactor也够用)。
但存在一些问题:
- 单个进程来执行事件侦听和事件处理,可能会给新的连接建立比较大的时延;
- 单个reactor来处理所有的事件侦听在高并发下可能会崩溃;
- 在Handler中将业务处理和一般性的读写动作混在一起,代码耦合性更高,也不利于业务逻辑的拓展;
- 没有充分发挥多核处理器的性能。
针对上述问题,便有了后续一些更多的reactor模式。
单reactor多线程模式

单reactor在IO处理单元上没有进行啥改变,还是单reactor模式。所不用的是使用了线程池来将具体的业务逻辑进行剥离。这里使用线程池来避免多线程每次创建线程和销毁线程的开销,同时使用线程相比较于进程也更方便的进行通信。实际中也看不到单reactor多进程的设计模式,因为子进程与父进程双向通信比较繁琐。
上述代码将业务逻辑从Handler中进行剥离,同时利用了多核,但IO部分的瓶颈还没解决,因此有多reactor多线程/进程模型。
多reactor多线程/进程模型

相比较于单reactor模式,这一版本有了两个变化。
- 每个连接都将获得一个reactor负责,mainreactor只负责listenfd的侦听和建立连接的分发;
- 将线程池改成了单个线程,每个线程负责一个连接。每个连接内部分为subreactor和handler两块,分别进行IO处理和具体事件处理。这一块没有将业务处理单独剥离为一个子线程/进程,因此不存在线程/进程间双向通信问题,处理起来更简单。memcache采用多reactor多线程方案,nginx采用多reactor多进程方案。
不过这部分没有将业务逻辑进行剥离的做法个人不太喜欢,应该是能改进的。
proactor模式
reactor是基于未完成IO事件
proactor是基于已完成IO事件

在 Linux 下的异步 I/O 是不完善的,aio
系列函数是由 POSIX 定义的异步操作接口,不是真正的操作系统级别支持的,而是在用户空间模拟出来的异步,并且仅仅支持基于本地文件的 aio 异步操作,网络编程中的 socket 是不支持的。因此现在讲proactor一般时说模拟proactor。这部分内容后续在详述。