swoole学习之: 协程基本概念之 Reactor(反应器)模式

0x00 两种体系结构

在处理web请求时,通常有两种体系结构,分别为:Thread-based architecture(基于线程)、Event-driven architecture(事件驱动)。

基于线程的体系结构 (Thread-based architecture)

最最原始的网络编程思路就是服务器用一个while循环,不断监听端口是否有新的套接字连接,如果有,那么就调用一个处理函数处理。

这种方法的最大问题是无法并发,效率太低,如果当前的请求没有处理完,那么后面的请求只能被阻塞,服务器的吞吐量太低。

之后,想到了使用多线程,也就是很经典的connection per thread,每一个连接用一个线程处理。对于每一个请求都分发给一个线程,每个线程中都独自处理上面的流程。tomcat服务器的早期版本确实是这样实现的。

这种基于线程的体系结构通常会使用多线程来处理客户端的请求,每当接收到一个请求,便开启一个独立的线程来处理。

这种方式虽然是直观的,但是仅适用于并发访问量不大的场景,因为线程需要占用一定的内存资源,且操作系统在线程之间的切换也需要一定的开销,当线程数过多时显然会降低web服务器的性能。

并且,当线程在处理I/O操作,在等待输入的这段时间线程处于空闲的状态,同样也会造成cpu资源的浪费。

一个典型的设计如下:

事件驱动体系结构 (Event-driven architecture)

事件驱动架构 (Event-driven architecture) 是一种软件体系范例,可促进时间的产生, 检测, 使用和响应。对于事件驱动系统而言,事件的捕获、通信、处理和持久保留是解决方案的核心结构。这和传统的请求驱动模型有很大不同。

这种方式会定义一系列的事件处理器来响应事件的发生,并且将服务端接受连接与对事件的处理分离。

许多现代应用都采用了事件驱动设计。因为事件驱动本身是一种编程方法,而不是一种编程语言, 因此事件驱动应用可以用任何一种编程语言来创建。事件驱动架构可以最大程度减少耦合度,因此是现代化分布式应用架构的理想之选。

事件驱动架构采用松散耦合方式,因为事件发起者并不知道哪个事件使用者在监听事件,而且事件也不知道其所产生的后续结果。

什么是事件?

事件, 是指系统硬件或软件的状态出现任何状态的重大改变。比如,tcp中socket的new incoming connection、ready for read、ready for write。

事件的来源可以是内部或外部输入。事件可以来自用户 (例如点击鼠标或按键)、外部源 (例如传感器输出) 或系统 (例如加载程序)。

事件与事件通知不同,后者是指系统发送的消息或通知,用于告知系统的其他部分有相应的事件发生。从形式上看,产生,发布,传播,检测或使用的是 (通常是异步的) 消息,称为事件通知,而不是事件本身,它是触发消息发出的状态更改。事件不会传播,它们只会发生。但是,术语“事件”通常被用作代名词来表示通知消息本身,这可能会引起一些混乱。这是由于通常在消息驱动的体系结构之上设计事件驱动的体系结构,在这种通信模式下,这种通信模式要求输入之一是纯文本消息 (消息),以区分应如何处理每种通信。

0x01 Reactor (反应器)模式

Reactor设计模式是Event-driven architecture的一种实现方式,处理多个客户端并发请求服务端的场景。

每种服务在服务端可能由多个方法组成, Reactor会解耦并发请求的服务并分发给对应的事件处理器来处理。

目前,许多流行的开源框架都用到了Reactor模式,如:大多数IO相关组件如Netty、Redis在使用的IO模式、linux上的libevent等,包括java的nio。

总体图示如下:

Reactor主要由以下几个角色构成:文件描述符 handle、同步事件分离器 Synchronous Event Demultiplexer、初始分发器 Initiation Dispatcher、事件处理器 Event HandlerConcrete Event Handler

1. 文件描述符 (Handle)

Handle在linux中一般称为文件描述符(File Descriptor),而在window称为句柄,两者的含义一样。Handle是事件的发源地。比如一个网络socket、磁盘文件等。而发生在handle上的事件可以有connection、ready for read、ready for write等。

2. 同步事件分离器 (Synchronous Event Demultiplexer)

本质上是系统调用。比如linux中的select、poll、epoll等。比如,select方法会一直阻塞直到handle上有事件发生时才会返回。

3. 初始分发器 (Initiation Dispatcher)

初始分发器,也是reactor角色,提供了注册、删除与转发event handler的方法。当Synchronous Event Demultiplexer检测到handle上有事件发生时,便会通知initiation dispatcher调用特定的event handler的回调方法。

4. 事件处理器 (Event Handler)

事件处理器,其会定义一些回调方法或者称为钩子函数,当handle上有事件发生时,回调方法便会执行,一种事件处理机制。

5. 具体的事件处理器 (Concrete Event Handler)

具体的事件处理器,实现了Event Handler。在回调方法中会实现具体的业务逻辑。

0x02 Reactor模式的处理流程

    1. 当应用向初始分发器(Initiation Dispatcher)注册具体的事件处理器(Concrete Event Handler)时,应用会标识出该事件处理器希望Initiation Dispatcher在某种类型的事件发生时向其通知,事件与handle关联。
  • 2. 初始分发器(Initiation Dispatcher)要求注册在其上面的具体的事件处理器(Concrete Event Handler)传递内部关联的handle,该handle会向操作系统标识。

    1. 当所有的具体的事件处理器(Concrete Event Handler)都注册到初始分发器(Initiation Dispatcher)上后,应用会调用handle_events方法来启动初始分发器(Initiation Dispatcher)的事件循环,这时初始分发器(Initiation Dispatcher)会将每个具体的事件处理器(Concrete Event Handler)关联的handle合并,并使用同步事件分离器(Synchronous Event Demultiplexer)来等待这些handle上事件的发生。
    1. 当与某个事件源对应的handle变为ready时,同步事件分离器(Synchronous Event Demultiplexer)便会通知初始分发器(Initiation Dispatcher)。比如tcp的socket变为ready for reading。
  • 5. 初始分发器(Initiation Dispatcher)会触发事件处理器的回调方法。当事件发生时, 初始分发器(Initiation Dispatcher)会将一个“key”(表示一个激活的handle)定位和分发给特定的事件处理器 (Event Handler)的回调方法。

  • 6. 初始分发器(Initiation Dispatcher)调用特定的具体的事件处理器(Concrete Event Handler)的回调方法来响应其关联的handle上发生的事件。

下图是一个来自网络的Reactor事件处理流程:

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值