【Reactor模型】事件驱动模型 - Reactor模型简述
Reactor模式
Reactor模式是非阻塞同步网络模式,主要由Reactor和处理资源池这两个核心部分组成,负责:
- Reactor负责监听和分发事件,事件类型包含连接事件,读写事件;
- 处理资源池负责处理事件,如read->处理逻辑->send
Reactor模式是灵活多变的,可以应对不同的业务场景,灵活在于:
- Reactor的数量可以只有一个,也可以有多个;
- 处理资源池可以是单个进程/线程,也可以是多个进程/线程
理论上有四种方案可选:
- 单Reactor单进程/线程
- 单Reactor多进程/线程
- 多Reactor单进程/线程
- 多Reactor多进程/线程
由于多Reactor单进程/线程没有性能优势,因此实际中并没有应用。剩下三个方案都比较经典,用处较多。
单Reactor单进程/线程
方案示意图:
可以看到进程中有Reactor、Accecptor、Handler这三个对象:
- Reactor对象的作用是监听和分发事件
- Acceptor对象的作用是获取连接
- Handler对象的作用是处理业务
对象里的select,accept,read,send是系统调用函数,dispatch和业务处理是需要完成的操作,其中dispatch是分发事件操作。
单Reactor单进程方案:
- Reactor对象通过select(IO多路复用接口)监听事件,收到事件后通过dispatch进行分发,具体分发给Acceptor对象还是Handler对象,要看事件类型;
- 如果是连接建立事件,则交由Acceptor对象进行处理,Acceptor对象会通知accept方法获取连接,并创建一个Handler对象来处理后续响应事件。
- 如果不是连接建立事件则交由连接对应的Handler对象来进行响应;
- Handler对象通过read->业务处理->send来完成完整的业务流程
方案缺点:
- 因为只有一个进程,无法充分利用多核CPU性能;
- Handler对象在业务处理时,整个进程是无法处理其他连接收件的,如果业务处理耗时比较长,就会造成响应的延迟。
所以单Reactor单进程的方案不适用于计算机密集型的场景,只是用于业务处理非常快速的场景。Redis就是采用的此种方案。
单Reactor多进程/线程
单Reactor多进程方案示意图:
单Reactor多线程方案描述:
- Reactor对象通过select(IO多路复用接口)监听事件,收到事件后通过dispatch进行分发,具体分发给Accept对象还是Handler对象,还要看收到的事件类型。
- 如果是连接建立的事件,则交由Acceptor对象进行处理,Acceptor对象会通过accept方法获取连接,并创建一个handler对象来处理后续的响应事件
- 如果不是连接建立事件,则交由当前连接对应的handler对象进行响应
- Handler对象不再负责业务处理,只负责数据的接收和发送,Handler对象通过read读取到数据后,会将数据发给子线程里的Processor对象进行业务处理;
- 子线程里的processor对象就进行业务处理,处理完成后,将结果发给主线程中的Handler对象,接着由Handler通过send方法将响应结果发送给client;
方案优势:
能够充分利用多核CPU的能力
方案缺点:
由于使用多线程,所以会带来多线程竞争资源的问题。比如:子线程在完成业余处理后,要把结果传递给主线程的Reactor进行发送,这里就涉及到共享数据的竞争。
单Reactor多进程方案:
相比多线程,多进程方案需要考虑子进程<->父进程的双向通信,并且父进程还需要知道子进程要将数据发送给哪一个客户端。因为多线程间可以共享数据,虽然要额外考虑并发问题,但是这远比进程间通信复杂度低的多。
单Reactor还存在一个问题,因为一个Reactor对象承担所有事件的监听和响应,而且只在主线程中运行,在面对瞬间高并发场景时,容易成为性能的瓶颈的地方。
多Reactor多进程/线程
多Reactor多线程方案示意图:
多Reactor多线程方案描述:
- 主线程中MainReactor对象通过select监控连接建立事件,收到时间后通过Acceptor对象中的accept获取连接,将新的连接分配给某个子线程;
- 子线程中的SubReactor对象将MainReactor对象分配到连接加入select继续进行监听,并创建一个Handler用于处理连接的响应事件
- 如果有新的事件发生,SubReactor对象会调用当前连接对应的Handler对象来进行响应
- Handler对象通过read->业务处理->send的流程来完成完整的业务流程。
多Reactor多线程方案相比单Reactor多线程方案实现要简单,原因如下:
- 主线程和子线程分工明确,主线程只负责接收新链接,子线程负责完成后续的业务处理。
- 主线程和子线程的交互很简单,主线程只需要把新连接传给子线程,子线程无需返回数据,直接就可以在子线程将处理结果发送给客户端
采用多Reactor多线程/进程方案的开源软件有Nginx
参考文章: