两种高性能 I/O 设计模式 Reactor 和 Proactor
两种高性能 I/O 设计模式 Reactor 和 Proactor
Reactor 和 Proactor 是基于事件驱动,在网络编程中经常用到两种设计模式。
曾经在一个项目中用到了网络库 libevent,也学习了一段时间,其内部实现所用到的就是 Reactor,所知道的还有 ACE;Proactor 模式的库有 Boost.Asio,ACE,暂时没有用过。但我也翻阅了一些文档,理解了它的实现方法。下面是我在学习这两种设计模式过程的笔记。
Reactor
Reactor,即反应堆。Reactor 的一般工作过程是首先在 Reactor 中注册(Reactor)感兴趣事件,并在注册时候指定某个已定义的回调函数(callback);当客户端发送请求时,在 Reactor 中会触发刚才注册的事件,并调用对应的处理函数。在这一个处理回调函数中,一般会有数据接收、处理、回复请求等操作。

libevent 采用的就是 Reactor 的设计思想。其 Reactor 的中心思想是众所周知的 I/O 多路复用:select,poll,epoll,kqueue 等.libevent 精彩的将定时事件,信号处理,I/O 事件结合在在一起,也就是说用户同时在 Reactor 中注册上述三类事件。遗憾的是,libevent 不支持多线程,也就是说它同步处理请求,导致不能处理大量的请求;这样并不是说 Reactor 实现的网络库都不支持多线程,而是 libevent 本身的原因,我们也可以通过修改让 ilbevent 支持多线程,并发处理多个请求。
下面是 libevent 的一段代码,大概能够说明 Reactor 工作模式:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/*accept callback function.*/void accept_callback(int fd, short ev,void *arg){ ......}......struct event accept_event;event_set(&accept_event, socketlisten, EV_READ|EV_PERSIST, accept_callback, NULL);event_add(&accept_event, NULL);event_dispatch(); |
Proactor
从上面 Reactor 模式中,发现服务端数据的接收和发送都占用了用户状态(还有一种内核态),这样服务器的处理操作就在数据的读写上阻塞花费了时间,节省这些时间的办法是借助操作系统的异步读写;异步读写在调用的时候可以传递回调函数或者回送信号,当异步操作完毕,内核会自动调用回调函数或者发送信号。Proactor 就是这么做的,所以很依赖操作系统。来一幅 UML:

和时序图:

注:这两幅美艳的图片来自 Proactor.doc,下面会提到.
Proactor 的实现主要有三个部分:异步操作处理器,Proactor 和 事件处理函数。其中:
- 异步操作处理器,很依赖操作系统的异步处理机制,如若操作系统没有实现,我们可以自行模拟,即开专门的数据读写线程,数据读写完毕触发相应的时间(如果有注册的话);
- Proactor,会接收异步操作的提醒,调用相应的事件处理函数,它有自己的 event loop;
- 事件处理函数,事件触发,执行操作;
曾经看过 Proactor.doc,作者是 Douglas C. Schmidt,你可以在这里阅读此文档。里面的关于 Proactor 的讲解很精彩,部分摘抄和自己的理解如下:当连接 web 服务器时:

- web 服务器指定(1)接收器,此接收器相当于服务器的客户端,它可以启动异步的 accept 操作;
- 接收器调用操作系统上的异步接收操作(2),并传递自己和 Proactor 的引用;异步接收操作结束后,前者用作事件处理函数,后者会回过头来分发事件;注:传递 Proactor 是为了让操作系统通知正确的 Proactor,可能会存在多个 Proactor;传递接收器自己是为了在异步接收操作结束后 Proactor 能调用正确的事件处理函数,以下同理。
- web 服务器调用 Proactor 的事件循环;(3)
- web 浏览器连接 web 服务器;(4)
- 异步接收操作结束后,操作系统产生事件(通过回调或者信号)并通知 Proactor(5),Proactor 收到后会调用相应的事件处理函数,即交由接收器处理;(6)
- 接收器生成 HTTP 处理器,执行操作;(7)
- HTTP 处理器解析事件,启动异步读操作(8),获取来自浏览器的 GET 请求。同样,HTTP 处理器传递自己和 Proactor 的引用;
- web 服务器的控制权交还回 Proactor 的事件循环。(9)
接收 GET 请求过后,会处理数据:

- 浏览器发送(1)一个 HTTP GET 请求;
- 异步读操作结束后,操作系统会通知 Proactor,Proactor 分发给事件处理函数;(2,3)
- 事件处理器解析请求。(4)2-4 步骤会重复,指导所有的数据都接收为止;
- 事件处理器产生答复数据;(5)
- HTTP 处理器启动异步写操作(6),传输应答数据,同样的这里还会传递处理器自己和 Proactor;
- 异步写操作结束,操作系统通知 Proactor(7),Proactor 分发给事件处理函数(8)。6-8 步骤会重复直到所有的数据写完为止。至此,一个请求回复完成。
总结
相比网络编程中最简单的思路模式:bind,listen,accept,read,server operator,write,Reactor 和 Proactor 是两种高性能的设计模式,掌握此两种模式,有助于理解一些网络库的工作流程。此文提到了两种设计模式,但没有一些技术细节,譬如多线程同步。如果在 Reactor 中支持多线程,或多个线程共享一个 Proactor,线程的同步问题就来了。共享一篇印象笔记关于线程的综合讨论:这里.
《Comparing Two High-Performance I/O Design Patterns》提到一个将 Reactor 模拟 Proactor 而不借助操作系统异步机制的方法:同样在 Reactor 注册感兴趣的事件(比如读),当事件发生时,执行非阻塞的读,读毕即才调用数据处理——假异步。
最后,实践出真知。欢迎讨论。
参考:
- Proactor.pdf,http://www.laputan.org/pub/sag/proactor.pdf
- 《Comparing Two High-Performance I/O Design Patterns》,http://www.artima.com/articles/io_design_patterns.html
- 《libevent源码深度剖析》,http://blog.csdn.net/sparkliang/article/details/4957667
- 《 IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)》,http://blog.csdn.net/historyasamirror/article/details/5778378
捣乱 2013-08-21
两种高性能 I/O 设计模式 Reactor 和 Proactor的更多相关文章
- I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor
目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...
- 两种高性能I/O设计模式(Reactor/Proactor)的比较
原文出处: Alex Libman 译文出处:潘孙友 欢迎分享原创到伯乐头条 综述 这篇文章探讨并比较两种用于TCP服务器的高性能设计模式. 除了介绍现有的解决方案,还提出了一种更具伸缩性,只 ...
- [转]两种高性能I/O设计模式(Reactor/Proactor)的比较
[原文地址:http://www.cppblog.com/pansunyou/archive/2011/01/26/io_design_patterns.html] 综述 这篇文章探讨并比较两种用于T ...
- 【转载】高性能I/O设计模式Reactor和Proactor
转载自:http://blog.csdn.net/roger_77/article/details/1555170 昨天购买了<程序员>杂志 2007.4期,第一时间去翻阅了一遍,其中有一 ...
- 高性能I/O设计模式Reactor和Proactor
系统I/O 可分为阻塞型, 非阻塞同步型,非阻塞异步型. (Linux对aio支持的不完整,所以linux上用Reactor比较多:Proactor需要系统API支持真正的“异步”) 阻塞型I/O意味 ...
- I/O模型系列之四:两种高性能IO设计模式 Reactor 和 Proactor
不同的操作系统实现的io策略可能不一样,即使是同一个操作系统也可能存在多重io策略,常见如linux上的select,poll,epoll,面对这么多不同类型的io接口,这里需要一层抽象api来完成, ...
- 高性能IO设计的Reactor和Proactor模式(转)
在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...
- I/O模型之四:Java 浅析I/O模型(BIO、NIO、AIO、Reactor、Proactor)
目录: <I/O模型之一:Unix的五种I/O模型> <I/O模型之二:Linux IO模式及 select.poll.epoll详解> <I/O模型之三:两种高性能 I ...
- 两种IO模式:Proactor与Reactor模式
在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...
随机推荐
- Log4j2 简明教程
一.概述 log4j2官方文档内容非常多,要一次性了解全部是不可能的.正确的步骤应当是先了解最常见的配置,当发现原有知识无法解决问题,再重新查看文档看有没有合适的配置.下面将从文件结构入手,再到简单的 ...
- HDU Billboard
题目分析:给你n张海报,一个宣传板.让你在满足海报能够贴在最高位置的时候则贴的最高,无法满足时贴的最靠左,输出海报所贴的高度.假设不能贴则输出-1. 一道非常easy,可是我没想出的基础线段树. 算法 ...
- C# WinForm开发系列 - WebBrowser
原文:C# WinForm开发系列 - WebBrowser 介绍Vs 2005中带的WebBrowser控件使用以及一些疑难问题的解决方法, 如如何正确显示中文, 屏蔽右键菜单, 设置代理等; 收集 ...
- 表单验证 jquery-validation
表单验证首选:jquery-validation 参见 http://jqueryvalidation.org/ 下载之后 在 demo 的index 页面有各种API 详细的案例. 还可以参考 h ...
- Oracle中的二进制、八进制、十进制、十六进制相互转换函数
原文:Oracle中的二进制.八进制.十进制.十六进制相互转换函数 Oracle中的二进制.八进制.十进制.十六进制相互转换函数 今天在网上看到一篇关于在oracle中对各种进制数进行转换的帖子, ...
- Asterisk 未来之路3.0_0006
原文:Asterisk 未来之路3.0_0006 Modules Asterisk 是基于模块构建的.一个模块提供某个特定的功能,它是动态的被装载.比如:信道驱动(chan_sip.so),或可以连接 ...
- SQL SERVER存储过程生成字母+数字的编码
公司内设备管理系统中设备建账功能,功能目的是对新进设备进行记录并入库.其中设备编号一项定义为自己修改(查看之前的设备号,取一个不重复的值来填写),感觉特别麻烦!用存储过程自动生成编码岂不是更效率. 需 ...
- 大约PF_RING/Intel 82599/透明VPN一些事
接近崩溃的边缘,如今,在医院这篇文章地方的想法,小病,我宁愿不吃药瓶.一台笔记本电脑,但无法上网,我不称职.想知道的东西.唯一可用3G,不开的热点.由于没人给我报销流程.这个周末,我只有一天,由于下雨 ...
- How do I create an IIS application and application pool using InnoSetup script
Create an IIS application. Create a new IIS application pool and set it's .NET version to 4. Set the ...
- Oracle常用函数:DateDiff() 返回两个日期之间的时间间隔自定义函数
首先在oracle中没有datediff()函数可以用以下方法在oracle中实现该函数的功能:1.利用日期间的加减运算天:ROUND(TO_NUMBER(END_DATE - START_DATE) ...