高并发服务器常由多线程+IO复用服务器(one event loop per thread)
两种I/O多路复用模式:Reactor和Proactor
一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数);事件分离器负责将请求事件传递给事件处理器。两个与事件分离器有关的模式是Reactor和Proactor。Reactor模式采用同步IO,而Proactor采用异步IO。
在Reactor中,事件分离器负责等待文件描述符或socket为读写操作准备就绪,然后将就绪事件传递给对应的处理器,最后由事件处理器负责完成实际的读写工作。而在Proactor模式中,处理器或者兼任处理器的事件分离器,只负责发起异步读写操作。IO操作本身由操作系统来完成。传递给操作系统的参数需要包括用户定义的数据缓冲区地址和数据大小,操作系统才能从中得到写出操作所需数据,或写入从socket读到的数据。事件分离器捕获IO操作完成事件,然后将事件传递给对应处理器。比如,在windows上,处理器发起一个异步IO操作,再由事件分离器等待IOCompletion事件。典型的异步模式实现,都建立在操作系统支持异步API的基础之上,我们将这种实现称为“系统级”异步或“真”异步,因为应用程序完全依赖操作系统执行真正的IO工作。以读操作为例:
在Reactor中实现读:
- 注册读就绪事件和相应的事件处理器
- 事件分离器等待事件
- 事件到来,激活分离器,分离器调用事件对应的处理器。
- 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。
在Proactor中实现读:
- 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
- 事件分离器等待操作完成事件
- 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。
- 事件分离器呼唤处理器。
- 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。
通过上例可以看出,两个模式的相同点,都是对某个IO事件的事件通知(即告诉某个模块,这个IO操作可以进行或已经完成)。在结构上,两者也有相同点:demultiplexor负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler;不同点在于,异步情况下(Proactor),当回调handler时,表示IO操作已经完成;同步情况下(Reactor),回调handler时,表示IO设备可以进行某个操作(can read or can write)。
使用Proactor框架和Reactor框架都可以极大的简化网络应用的开发,但它们的重点却不同。Reactor框架中用户定义的操作是在实际操作之前调用的。比如你定义了操作是要向一个SOCKET写数据,那么当该SOCKET可以接收数据的时候,你的操作就会被调用;而Proactor框架中用户定义的操作是在实际操作之后调用的。比如你定义了一个操作要显示从SOCKET中读入的数据,那么当读操作完成以后,你的操作才会被调用。
Proactor和Reactor都是并发编程中的设计模式。在我看来,他们都是用于派发/分离IO操作事件的。这里所谓的IO事件也就是诸如read/write的IO操作。"派发/分离"就是将单独的IO事件通知到上层模块。两个模式不同的地方在于,Proactor用于异步IO,而Reactor用于同步IO。目前应用最广泛的是Reactor模boost::asio,ACE和Windows I/O Completion Ports 实现了Proactor 模式,应用面似乎要窄一些。
Proactor (论文翻译提要)
原文: http://www.cs.wustl.edu/~schmidt/PDF/proactor.pdf
结构
1. proacitve initiator (Web server application’s main thread)
proactive Initiator是应用中启动异步操作的实体.proactive initiator注册一个completion handler和一个completion dispatcher,以及一个asynchronous Operation Processor, asynchronous Operation Processor的作用是当操作完成将会通知.
2.Completion Handler(the acceptor and http handler)
Proactor模式使用completion handler接口用来获得异步操作完成的通知.
3.Asynchronous Operation
异步操作,由系统API实现,如async_read,async_write,async_accept等.异步操作用来执行异步请求(如i/o或定时器.).当应用请求异步操作时,该操作不会占用应用(主)线程.因此,从整个应用的角度来看,操作表现为异步.当异步操作完成,异步操作处理器(Asynchronous Operation Processor)将通知完成操作分发器(Completion Dispatcher)
4. Asynchronous Operation Processor(操作系统)
异步操作由异步操作处理器(Asynchronous Operation Processor)执行完成.该组件由操作系统实现.
5. Completion Dispatcher (the Notification Queue)
完成分发器的作用是,当异步操作完成时,把Completion handler返回给应用,即proacitve initiator
流程
1.主动启动器(proacitve initiator)启动操作
为了表现为异步操作,应用在异步操作处理器中启动操作.举例:一个web server向OS请求通过网络向某个特定的socket connetion发送一个文件.web server应该指定当操作完成去通知哪一个completion handler,以及当文件传送完成后哪一个completion dispatcher去callback.
2.异步操作处理器执行操作.
当应用在异步操作处理器上请求操作,异步操作处理器异步地执行这些操作.现代操作系统在内核中提供了异步IO.
3.异步操作处理器通知完成分发器
当异步操作完成,异步操作处理器检索特定的completion handler和completion dispatcher.(在第1步时指定的).然后异步操作处理器把异步操作的结果(the result of the Asynchronous Operation)和要返回的comletion handler(the Completion Handler to call back)传递给completion dispatcher.举例:当文件成功地被异步发送出,异步操作处理器将会报告完成状态(成功或失败),同时the number of bytes written to the network connection.
4.完成分发器通知应用程序.
举例:例如一个异步读操作完成后,completion handler 将被传入一个指向newly arrived data的指针.
以下是POSA2中的proactor模型
http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/async.html
https://segmentfault.com/a/1190000002715832
多了一个proactor,completion dispatcher 改为completion event queue.
流程
- Initiator启动proactor.proactor内是event loop等待完成事件的到来.
- 有请求到来,initiator请求asy operation processor执行异步操作.异步操作完成后,将结果返回给asy operation processor.asy operation processor将结果放入到completion event queue.
- Proactor进行event loop,等待completion event的到来.当queue中存在completion event,proactor获得此完成事件并把事件结果传递给completion handler
以主动写为例:
- Reactor将handle放到select(),等待可写就绪,然后调用write()写入数据;写完处理后续逻辑;
- Proactor调用aoi_write后立刻返回,由内核负责写操作,写完后调用相应的回调函数处理后续逻辑;