基于I/O复用的Reactor模式

目录

I/O模型的选择

1.阻塞式I/O模型:

2.非阻塞式I/O模型:

3.问题的关键:

4.I/O复用模型

4.1Reactor模式介绍

4.2Reactor模式组成

4.3Reactor事件处理流程


I/O模型的选择

在网络编程中,服务端一般需要判断是否存在多个可读的客户端请求,处理请求的方式主要可以分为以下几种。

1.阻塞式I/O模型:

对每个连接套接字通过轮流read系统调用获取可读数据。read系统调用将会把该线程阻塞,直到数据报到达且被复制到应用进程的缓冲区中时才会返回。因此不适用多客户端连接的情况。

图1 阻塞式I/O模型

2.非阻塞式I/O模型:

进一步优化,虽然数据可读和读取数据这两个操作依旧在一个系统调用中,但是如果没有数据可读,系统调用将立即返回。因此,通过轮询的方式可以初步解决多客户端连接的问题。缺点,持续轮询消耗大量CPU时间,服务器开销大。

更进一步,加入多线程的支持,每个客户端分配一个线程处理,读写操作由线程完成。但是该方案受到线程数的限制,同时线程的创建和消毁开销大(利用线程池可以优化),对于几千个以上的客户端连接,操作系统调度存在负担。

图2 非阻塞式I/O模型

3.问题的分析:

上述两个模型都不能完美的适用于高并发的网络服务器,原因有二:

1.需要将数据可读判断与实际读取数据操作分离

2.能够同时支持多个套接字可读判断

因此我们需要一种能够预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪,即输入数据已经准备好被读取,它就通知进程,该行为称之为I/O复用。

在Linux平台上,提供了select、poll和epoll这几种系统调用作为I/O复用的方式。

 

4.I/O复用模型

我们将多个连接套接字注册在I/O复用的epoll系统调用中,并注册读事件,此时系统阻塞于epoll调用,等待某个数据报套接字变为可读。当epoll返回时,将会返回可读的套接字集合,我们只需遍历这些可读套接字,然后分别对每个可读套接字调用read系统调用,读出具体数据,并进一步进行相应处理即可。

图3 Reactor事件处理流程

4.1Reactor模式介绍

问题抽象:
       每个已经连接的套接字描述符就是一个事件源,每一个套接字接收到数据后的进一步处理操作作为一个事件处理器。我们将需要被处理的事件处理源及其事件处理器注册到一个类似于epoll的事件分离器中。事件分离器负责等待事件发生。一旦某个事件发送,事件分离器就将该事件传递给该事件注册的对应的处理器,最后由处理器负责完成实际的读写工作。

Reactor模式是一种以事件驱动为核心的机制。在Reactor模式中,应用程序不是主动的调用某个API完成处理动作,而是逆置了事件处理流程。应用程序只需要提供相应的事件接口并注册到Reactor上,当相应的事件发生时,Reactor就会主动调用应用程序注册的接口,通过注册的接口完成具体的事件处理。

4.2Reactor模式组成

Reactor模式由事件源、事件反应器、事件分离器、事件处理器组成,其类图结构如下:

图4 Reactor类图

 

  • 事件源(handle):由操作系统提供,用于识别每一个事件,如Socket描述符、文件描述符等。在服务端系统中用一个整数表示。该事件可能来自外部,如来自客户端的连接请求、数据等。也可能来自内部,如定时器事件。
  • 事件反应器(reactor):定义和应用程序控制事件调度,以及应用程序注册、删除事件处理器和相关描述符相关的接口。它是事件处理器的调度核心,使用事件分离器来等待事件的发生。一旦事件发生,反应器先是分离每个事件,然后调度具体事件的事件处理器中的回调函数处理事件。
  • 事件分离器(demultiplexer):是一个有操作系统提供的I/O复用函数,在此我们选用epoll。用来等待一个或多个事件的发生。调用者将会被阻塞,直到分离器分离的描述符集上有事件发生。
  • 事件处理器(even handler):事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供reactor在相应的事件发生时调用,执行相应的事件处理。一般每个具体的事件处理器总是会绑定一个有效的描述符句柄,用来识别事件和服务。

4.3Reactor事件处理流程

Reactor事件处理流程如图4所示,它由事件注册和事件分发两部分组成,具体论述如下。

 

图5 Reactor事件处理时序图

在事件注册部分,应用程序首先将期待注册的套接字描述符作为事件源,并将描述符和该事件对应的事件处理回调函数封装到具体的事件处理器中,并将该事件处理器注册到事件反应器中。事件反应器接收到事件后,进行相应处理,并将注册信息再次注册到事件分离器epoll中。最后在epoll分离器中,通过epoll_ctl进行添加描述符及其事件,并层层返回注册结果。

在事件处理部分,首先事件反应器通过调用事件分离器的epoll_wait,使线程阻塞等待注册事件发生。此时如果某注册事件发生,epoll_wait将会返回,并将包含该注册事件在内的事件集返回给事件反应器。反应器接收到该事件后,根据该事件源找到该事件的事件处理器,并判断事件类型,根据事件类型在该事件处理器调用之前注册时封装的具体回调函数,在这个具体回调函数中完成事件处理。

  根据Reactor模式具体的事件处理流程可知,应用程序只参与了最开始的事件注册部分。对于之后的整个事件等待和处理的流程中,应用程序并不直接参与,最终的事件处理也是委托给了事件反应器进行。因此通过使用Reactor模式,应用程序无需关心事件是怎么来的,是什么时候来的,我们只需在注册事件时设置好相应的处理方式即可。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值