现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

陈硕:ACE的历史与简评

2014-10-09 20:50 工业·编程 ⁄ 共 8058字 ⁄ 字号 评论 20 条

    ACE 是现代面向对象网络编程的鼻祖,确立了许多重要模式,如 Reactor、Acceptor 等,重要到我们甚至觉得网络编程就应该是那样的。但为什么 ACE 叫好不叫座?大名鼎鼎却使用者寥寥?本文谈谈我的个人观点。

    ACE 是一套重量级的 C++ 网络库,早期版本由 Douglas Schmidt 独自开发,后来有 40 余名学生与工作人员也贡献了大量代码。作者 Douglas Schmidt 凭借它发表了 30 余篇学术论文。ACE 的一大特点是融合了 Douglas Schmidt 提出的很多面向对象网络编程的设计模式,并且具有不可思议的跨平台能力

1 ACE 历史

先说说 ACE 之父 Douglas Schmidt 的个人经历

    1990 年在加州大学 Irvine 分校获计算机硕士学位;

    1994 年在同一学校获计算机博士学位,论文《An Object-Oriented Framework for Experimenting with Alternative Process Architectures for Parallelizing Communication Subsystems》。从论文内容看,主要工作就是后来大名鼎鼎的 ACE framework,文中叫 ASX framework。

    1994 年博士毕业后前往华盛顿大学任助理教授,后升至副教授

    2003 年起在 Vanderbilt 大学任正教授至今

        我相信 ACE 是 Douglas 在读博期间的主要工作,ACE 这个名字最早出现在 1993 年 12 月的一篇会议论文上,Douglas 的这篇文章获得了“最佳学生论文”奖。在此之前,Douglas 已经用 ASX 等其他名字发表了内容相近的文章。

        我能下载到的最早的 ACE 版本是 4.0.32,有大约 86,000 行 C++ 代码,代码的时间戳是 1998 年 10 月 22 日。早期 ACE 由 Douglas Schmidt 个人独立开发,从 ChangeLog 得知,1993 年 11 月 ACE 的版本号是 2.12。到了 1995 年 9 月,才有第一次出现其他开发者。在 1993~1996 年间的 684 次改动中,Douglas 一个人贡献了 529 次,另外几个主要开发者以及他们的修改次数分别是 Prashant Jain (58)、Tim Harrison (42)、David Levine (28)、Irfan Pyarali (20)、Jesper S. M|ller (5)。

        从整个 ChangeLog 看,从 1993 年到 2010 年 3 月有 19,000 余次改动。有超过 200 人修改过代码,其中 23 个人的 check-in 次数大于 100,排名前 12 的代码修改者为:

           3635  Johnny Willemsen (活跃年份:2001~今)

           2586  Douglas C. Schmidt(原作者,活跃年份:1993~今)

           1861  Steve Huston (活跃年份:1997~今)

           1197  David L. Levine  (活跃年份:1996~2000)

            962  Nanbor Wang  (活跃年份:1998~2003)

            907  Ossama Othman (活跃年份:1999~2005)

            865  Chad Elliott (活跃年份:2000~今)

            823  Bala Natarajan (活跃年份:1999~2004)

            708  Carlos O'Ryan (活跃年份:1997~2001)

            544  J.T. Conklin (活跃年份:2004~2008)

            479  Irfan Pyarali (活跃年份:1996~2003)

            368  Darrell Brunsch (活跃年份:1997~2001)

        看到这些“活跃年份”,你的第一反应是什么?我想到的是,这些人会不会多半是 Douglas 指导的研究生?我猜他们在读研期间参与改进 ACE,把工作内容写成论文发表,然后毕业走人。或许这能解释 ACE 代码风格的多样性。

        在浏览代码历史的过程中,我还发现一个很有意思的现象,在 2008 年 3 月 4 日,某人不小心把整个 ACE 的源代码树删除了:

        https://svn.dre.vanderbilt.edu/viewvc/Middleware?view=revision&revision=80824

        随后又很快恢复:

        https://svn.dre.vanderbilt.edu/viewvc/Middleware?view=revision&revision=80826

        干这件事情的老兄在 2005~2009 这几年里一共 check in 了 120 余次。你对这件事情怎么看?你们的开发团队里有这样的人吗?

        2 事实与思考

        1. 除了 Douglas Schmidt 和 Stephen Huston 写的三本书籍之外,没有其他专著讲 ACE。

        究竟是 ACE 太好用了,以至于无需其他书来讲解,还是太难用了,讲也讲不明白?抑或根本就没人在乎?

        C++ 网络编程 第1卷》《C++ 网络编程 第2卷》《ACE 程序员指南》这三本书先后于 2001、2002、2003 年出版,之后再无更新。在同一时期,同样在网络编程领域,尽管 W. Richard Stevens 在 1999 年去世,他的 UNP 和 APUE 仍然由别人续写了新版。讲 C 语言 Sockets API 的书尚且不断更新,上层封装的 C++ 居然无动于衷?真的是封装到位了,屏蔽了这些变化?

        UNP 的可操作性很强,读前面几章,就能上手编写简单的网络程序,看完大半本书,网络编程基本就算入门了,能编写一般应用的网络程序。相反,读完 ACE 那几本书,对于简单的网络编程任务还是感觉无从下手,这是因为书写得不好,还是 ACE 本身不好用?

        2. ACE 很难用,非常容易用错

        我不止听到一个人对我说,他们在项目里尝试过 ACE,不是中途放弃,因为出了问题无法解决;就是勉强交差,并且从下一个项目起坚决不用。我听到的另一个说法是,ACE 教程的例子必须原封不动地抄下来,改一点点就会出漏子。不巧的是,ACE 的例子举来举去就是个 Logging 服务器,让人想照猫画虎也无从下手。在最近的《代码之美》一书中,Douglas Schmidt 再次拿它为例,说明他真的很喜欢这个例子。

        用 ACE 编程如履薄冰,生怕在阴沟里翻船,不知道它背后玩了什么把戏。相反,用 10 来个 Sockets 系统调用就能搞定网络编程,我感觉比使用 ACE 难度要小。为什么“高级”工具反而没有低级工具顺手呢?

        不好用的直接后果是少有人用,放眼望去,目前涉及网络的 C++ 开源项目里边,鲜有用 ACE 作为通信平台的(我知道的只有 Mangos)。相反,libevent 这个轻量级的 IO multiplexing 库有 memcached 这样的著名用户。

        3. ACE 代码质量不高,更像是一个研究项目,而不是工业界的产品

        读 ACE 现在的代码,一股学生气扑面而来,感觉像在读实习生写的代码。抛开编码风格不谈,这里举三个“硬伤”:

          sleep < 2ms

              在某些早期的 Linux 内核上,如果 select/poll 的等待时间小于 2ms,内核会采用 busy-waiting。这是极大的 CPU 资源浪费,而 ACE 似乎没有考虑避免这一点。

                Linux TCP self-connection

                    Linux 的 TCP 实现有一个特殊“行为”,在某些特殊情况下会发起自连接。而 Linux 网络协议栈的维护者认为这是一个 feature,不是 bug,拒绝修复。通常网络应用程序不希望出现这种情况,我见过的好的网络库会有意识地检查并断开这种连接,然而 ACE 漠然视之。

                      timeval on 64-bit

                          ACE_Time_Value 类直接以 struct timeval 为成员变量,保存从 Epoch 开始的微秒数。这在 32-bit 下没问题,对象大小是 8 字节。到了 LP64 模式的 64-bit 平台,比如 Linux,对象大小变为 16 字节,这么做就不够好了。我们可以直接用 int64_t 来保存这个以微秒为单位的时间,64-bit 整数能存下上下 30 万年,足够用了。减小对象大小并不是为了节约几个字节的内存,而是方便函数参数传递。在 x86-64 上,这种 8 字节的结构体可以用 64-bit 寄存器直接传参,也就是说 pass by value 会比 pass by reference 更快。对于一般的应用程序而言,要不要这么做值得商榷。对于底层的 C++ 网络库,不加区分地使用 pass by reference 会让人怀疑作者知其然不知其所以然。

                          对于以上几点情况,我怀疑 ACE 根本没用在 Linux 大规模生产环境下使用过,我只能期望它在别的平台表现好一些了。ACE 的作者们似乎更注重验证新想法,然后发论文,而不是把它放到工业环境中反复锤炼,打造为靠得住的产品。(类似 Minix 与 Linux 的关系。)

                          4. 移植性很好,支持我知道的和不知道的很多平台

                          ACE 的意义在于让我们明白了C++代码可以做到可移植,并展示了这么做会付出多么巨大的代价。不细说了,读过 ACE 代码的人都明白。

                          从代码质量上看,ACE 做到了能在这些平台上运行,但似乎没有在哪个平台占据主导地位。有没有哪个平台的网络编程首选 ACE?

                          出现这一状况的原因是,跨平台和高性能是矛盾的。跨平台意味着要抽象出多个平台的共性,以最 general 的方式编写上层代码。而高性能则要求充分发挥平台的特性,剑走偏锋,用尽平台能提供的一切加速手段,哪怕与其他平台不兼容。网络编程对此尤为敏感。

                          我不知道 ACE 的性能如何,因为在各项性能评测榜上基本看不到它的名字(c10k 里就没有 ACE 的身影)。另外,Buffer class 的好坏直接反应了网络库对性能的追求,ACE 提供了比 std::deque<uint8_t> 更好的输入输出 Buffer 吗?(我不是说 deque 有多好,它基本是 fail-safe 的选择而已。)

                          5. ACE 过于复杂,甚至比它试图封装的对象更复杂。

                          (这里的代码行数均为 wc 命令的粗略估计。)

                          ACE 5.7 自身(不含 TAO 和 CIAO)有 30 万行 C++ 代码(Douglas 自己给出的数据是 25 万行,可能指的是略早的版本),这是一个什么概念呢?我们来看 TCP/IP 协议栈本身的实现有多少行:(均不含 IPv6)

                            TCPv2 列出的 BSD4.4-Lite 完整 TCP/IP 协议栈代码有 15,000 行,其中 4,500 行 TCP 协议,800 行 UDP 协议,2,500 行 IP 协议

                            Linux 1.2.13 完整的 TCP/IP 协议栈有 2 万多行 (net/inet)

                            Linux 2.6.32.9 的 TCP/IP 协议栈有 6 万多行 (net/ipv4)

                            FreeBSD 8.0 的 TCP/IP 协议栈有 5 万多行 (sys/netinet, 不含 sctp)

                                换句话说,ACE 用 30 万行 C++ 代码“封装”了不到 10 万行 C 代码(且不论 C++ 代码的信息密度比 C 大),这是不是头重脚轻呢?我理解的“封装”是把复杂的东西变简单,但 ACE 好像走向了另一个方向,把不那么复杂的东西变复杂了。

                                这个对比数字可能不太准确,因为 ACE 还封装了很多其他东西,请看。http://www.dre.vanderbilt.edu/Doxygen/5.7.7/html/ace/inherits.htmlhttp://www.dre.vanderbilt.edu/Doxygen/5.7.7/html/ace/hierarchy.html

                                以下两张类的继承关系图片请在新窗口打开:

                                http://www.dre.vanderbilt.edu/Doxygen/5.7.7/html/ace/a06178.png

                                http://www.dre.vanderbilt.edu/Doxygen/5.7.7/html/ace/a06347.png

                                Douglas 说 ACE 包含了 40 人年的工作量,对此我毫不怀疑。但是,网络编程真的需要这么复杂吗?TCP/IP 协议栈的实现也没这么多工作量嘛。或许只有 CORBA 这样的应用才会用到这么复杂的东西?那么为什么 ICE 在重新实现 CORBA 的功能时没有基于 ACE 来写呢?是不是因为 ACE 架子拉得大,底子并不牢?

                                3 ACE 的意义

                                ACE 对于面向对象、设计模式和网络编程具有重大历史和现实意义。

                                ACE 诞生之时,正是 90 年代初期面向对象技术的高速发展期,ACE 一定程度上是作为面向对象技术的成功案例来宣传的。

                                在 1994 年前后,Unix 分为两个阵营,AT&T 的 SVR4 与 BSD 的 BSD4.x,这两家的 IO multiplexing 不完全兼容。比如 SVR4 提供 poll 调用,而 BSD 提供 select 调用。ACE 当时的宣传点之一是用面向对象技术屏蔽了两个平台的差异,提供了统一的 Reactor 接口。

                                【接下来,poll 在 1996 年 9 月 7 号加入 NetBSD,并随 NetBSD 1.3 于 1998 年 1 月 4 号发布。随后 FreeBSD 3.0 也支持 poll,1998 年 10 月发布。Linux 很早就支持 select,从 2.1.23 内核起支持 poll,发布日期为 1997 年 1 月 26 号。也就是说,到了 1998 年,平台差异被暂时抹平了。随后 epoll、/dev/poll、kqueue 以性能为名,再次扩大了平台差异。当然,Windows 至今不支持 poll。】

                                ACE 的设计似乎过于强调面向对象的灵活性,一些不该使用虚函数的地方也提供了定制点,比如 ACE_Timer_Queue 就应是个具体类,而不是允许用户 override schedule/cancel/expire 之类的具体操作。面向对象中,“继承”的目的是为了被复用,而不是去复用基类的代码。

                                查其文献,Reactor 在 1993 年登上《C++ Report》杂志的时候,文章标题还比较朴素,挂着“面向对象”的旗号:

                                  The Reactor: An Object-Oriented Interface for Event-Driven UNIX I/O Multiplexing (Part 1 of 2)

                                  The Object-Oriented Design and Implementation of the Reactor: A C++ Wrapper for UNIX I/O Multiplexing (Part 2 of 2)

                                      转眼到了 1994 年,也就是《设计模式》成书的那一年,Douglas 开始写文章言必称 pattern:

                                        Reactor 变成了 pattern,收录于《Pattern Languages of Program Design》一书(An Object Behavioral Pattern for Demultiplexing and Dispatching Handles for Synchronous Events)。这篇文章比前面两篇难懂,如果直接阅读的话。

                                        Acceptor 是 pattern (A Design Pattern for Passively Initializing Network Services),

                                        Connector 也是 pattern(A Design Pattern for Actively Initializing Network Services),

                                        Proactor 还是 pattern(An Object Behavioral Pattern for Demultiplexing and Dispatching Handlers for Asynchronous Events),

                                        居然连 Thread-Specific Storage 都成了 pattern(An Object Behavioral Pattern for Accessing per-Thread State Efficiently)。

                                        还有 Non-blocking Buffered I/O,也是 pattern (An Object Behavioral Pattern for Communication Gateways)。

                                            似乎 "pattern" 这个字样成了发文章的通行证,这股风气直到 2000 左右才刹住。之后这些论文集结出版,以《Pattern-Oriented Software Architecture》为名出了好几本书,ACE 的内容主要集中在第二卷。(请留意,原来的提法是 Object-Oriented,现在变成了 Pattern-Oriented,似乎软件开发就应该像糖果厂生产绿豆糕,用模子一个个印出来完事。)

                                            ACE 就像一个 pattern 大观园,保守估计有 10 来种 patterns 藏身其中,形成了一套模式语言(《Applying a Pattern Language to Develop Application-level Gateways》),这还不包括 GoF 定义的一般意义下的 OO pattern。

                                            通过 ACE 来学习网络编程,那是本末倒置,因为它教不了你任何 UNP 以外的知识。(Windows 网络编程?)

                                            然而,如果要用面向对象的方式来搞网络编程,那么 ACE 的思想(而不是代码)是值得效仿的,毕竟它饱含了 Douglas Schmidt 等学者的心血与智慧。学得好的例子有 Apache Mina、JBoss Netty、Python Twisted、Perl POE 等等。

                                            这就是我说“学之者生,用之者死”的含义。

                                            4 ACE 文献导读

                                            Douglas Schmidt 写了很多 ACE 的文章,其中不乏内容相近的作品。读他的文章,首选发表在技术杂志上的文章(比如 C++ Report),而不是发表在学术期刊或会议上的论文。前者的写作目的是教会读者技术,后者则往往是展示作者的新思路新想法,技术文章比学术论文要好读得多。

                                            由于当时面向对象技术尚在发展,Douglas 文章里的图形很有特色,不是现在规范的 UML 图(那会儿 UML 还没定型呢),而是像变形虫一样的类图(经pinxue指出,这种图是 Grady Booch 发明的),放在一堆文献里也很容易认出来。

                                            如果要用 ACE 的代码来验证文章的思路,我建议阅读和文章同时期的 4.0 版本代码,代码风格比较统一,代码量也不大,便于理解。

                                            下面介绍几篇有代表性的论文。

                                              1993 年 12 月第 11 届 SUG 会议,《The ADAPTIVE Communication Environment: Object-Oriented Network Programming Components for Developing Client/Server Applications》,获得最佳学生论文奖。这是我找到的最早一篇以 ACE 为题的论文。

                                              1994 年 6 月第 12 届 SUG 会议,《The ADAPTIVE Communication Environment: An Object-Oriented Network Programming Toolkit for Developing Communication Software》,获得最佳学生论文奖。

                                                  以上两篇文章实际上内容基本相同,都是对 ACE 的概要介绍,看第二篇即可,第一次没看懂也没关系。

                                                  剩下要看的是一篇 Socket OO 封装、四篇 Reactor、三篇 Acceptor-Connector、一篇 Proactor。这些文章前面大多都给了链接,其余的这里补充一下:

                                                    IPC_SAP: A Family of Object-Oriented Interfaces for Local and Remote Interprocess Communication

                                                    The Design and Use of the ACE Reactor

                                                    Acceptor and Connector -- A Family of Object Creational Patterns for Initializing Communication Services  这篇论文其实可以不用看,因为它不过是把前面两篇发表在 C++ Report 上的文章合到了一起。

                                                        不想看这 10 篇论文的话,读中译本《C++ 网络编程 第1卷》《C++ 网络编程 第2卷》《ACE 程序员指南》也行,翻译质量都不错。

                                                        5 设想中的 C++ 网络库

                                                        与文章主旨无关,略。

                                                        我觉得网络库要解决现实的问题,满足现实的需要,而不是把 features/patterns 堆在那里等别人来用。应该先有应用,再提炼出库。而不是先造库,然后寻求应用。

                                                        来源:陈硕的Blog

                                                        目前有 20 条留言    访客:18 条, 博主:0 条 ,引用: 2 条

                                                        1. 爱求索 2014年10月12日 8:28 上午  @回复  Δ1楼 回复

                                                          c++的另一个网络库boost.asio正是一个 library,而不是 framework,不过又走向另外一个极端,过度使用模板,一个虚函数都没有,所有实现全部暴露在头文件中(不知道这到底是优点还是缺点)。我觉得他们最大的缺点还是“过度”:过度设计,过度使用面向对象的技术。

                                                        2. 爱求索 2014年10月12日 8:33 上午  @回复  Δ2楼 回复

                                                          你说虚函数的问题,其实 他里面因为虚函数影响性能的地方 还真没几个 。再说 网络程序 性能无疑是 很重要的,但是 是唯一吗? 再者 我们自己搞的 也许并不如它

                                                          ACE 在LINUX 下的表现 据说 确实有不好之处。。
                                                          金山的词霸 用了ACE。。

                                                          • 爱求索 2014年10月12日 8:33 上午  @回复  ∇地下1层 回复

                                                            问题就在于“ACE 提供的东西不够好”。我反对在接口中暴露虚函数并不是因为性能原因,请见本博《以boost::function和boost:bind取代虚函数》

                                                        3. 爱求索 2014年10月12日 8:43 上午  @回复  Δ3楼 回复

                                                          在很多公司,还有很多项目,有一种叫基础库的东东,我看到很多公司用一些“牛人”所谓的基础库,然后人走了,也没人维护了。
                                                          也有很多公司用一些市面上的所谓的“强大的网络库”,但用着用着,发现需要某种功能的时候却没有。

                                                          在我看来,ace是当前c++功能最强大的库,各方面的控制也还说得过去,(也有人认为是asio,我觉得仁者见仁)。

                                                          选用其他的库经常会碰到一些维护的人少,开发的人水平本来就不怎么样这些事情(c++轮子很多,我以前刚毕业的时候也不自量力想搞一套很强大的网络库,后来觉得自己天资不行)

                                                          ace是一个开源方案的拆中选择,强大而复杂,虽然楼主认为用的人少(但其他的用的人其实更少),社区用户和开发者比较活跃。而且也有专门的公司做支持。相对风险略为可控(就是说最多就是钱的问题)

                                                          批评容易建设难,一个东东很容易看到他的缺点,但要用东东来替代,有时候还真难。

                                                        4. 爱求索 2014年10月12日 8:46 上午  @回复  Δ4楼 回复

                                                          ACE是网络编程中经验的积累,如果有比较丰富的经验,那些模式自己也可以总结出来,但是如果经验不够,这些模式是看不明白也用不好的,然而真正有了水平的人也是不屑用ACE这样既庞大有复杂的库的,所以博主说学之者生,用之者死我是非常赞同的,自身水平不怎么样还想驾驭庞大的系统,不死还能怎么样?

                                                        5. 爱求索 2014年10月12日 9:14 上午  @回复  Δ5楼 回复

                                                          ACE在工业界还是得到比较多应用的,特别是外企,而且作为底层中间件用了很多年也比较稳定,更不大可能会被替换,因为成本太大。而要用好它确实需要对其设计思想和实现有全面深刻的理解,懂得扬长避短。不然一些小陷阱会让人很郁闷。

                                                        6. 爱求索 2014年10月12日 9:15 上午  @回复  Δ6楼 回复

                                                          我把ACE等成c++世界的jdk来用。再加上boost和标准c++以及其他的开源库,基本上就可以做到跨平台c++编程了。想想吧,这样多好阿。起码比java性能快多了。如果水平足够高,时间也足够,就算这些库有什么缺点,总有办法去解决的。
                                                          楼主评说ACE的优缺点,但是也不能直接下结论这个东西不行,用了就死。实在不行,你就拿代码去借鉴,改成自己的东西。ACE不限制你任何行为。

                                                        7. 爱求索 2014年10月12日 9:16 上午  @回复  Δ7楼 回复

                                                          用过3-5年ACE的飘过,对于楼主的牛气,我只能说大哥你太高了,高到我都看不见了,ACE的确有很多不尽如人意的地方,比如学习曲线比较陡峭,代码里面有不尽如人意的地方,但是仅仅因为存在缺点就全盘否定我只能说你也就是个盲人摸象的主,不如楼主来开发出一套取代ACE的网络库吧,不要在这里喷口水了,那将是开发人员的一大幸事

                                                        8. 爱求索 2014年10月12日 9:17 上午  @回复  Δ8楼 回复

                                                          一个事物能够存在并发展这么多年,必定是有其特别之处的。以本人经历来说,工作中有过三次体验。前两次均以失败告终,因为那只是把NP1,NP2的例子按照自己的理解来用,至于它内部的实现机制如何并未深究。之前有位说文件传输效率巨差的我看到那也笑了下,当时确实没办法,只好用socket api重写了事。但庆幸的还好没有放弃对ACE的钻研,第三次使用时也具备了设计模式的知识,基于ACE的项目还是比较成功的。系统运行至今良好,没有出过错,不知道这能不能算为成功?第三次使用时其实很简单,只是把第一、二次实践中写出来的代码删除了三分之二,把剩下的三分之一重新排列组合了一下而已。但我不知道我的情形是要归之于学之还是用之呢。我还是希望第一次尝试就放弃的同仁们能再坚持一下吧。

                                                        9. 爱求索 2014年10月12日 9:18 上午  @回复  Δ9楼 回复

                                                          ace对应用c++来开发是很具有学习价值的。在hw的多年,时时刻刻都在和ace打交道,它的设计思想、模式、观念都是值得学习。对ace的评论还是在有了一定了解后在做评价为好。

                                                        10. 爱求索 2014年10月12日 9:19 上午  @回复  Δ10楼 回复

                                                          真是公说公有理,婆说婆有理。我们现在也在用ACE,一个服务器通过ACE来接收上千台设备传输的数据,我当初提议我们这个项目用ACE也只是考虑到跨平台的问题,我们在Windows和Linux Vxworks上都需要用。由于目前只是在开发阶段,能充分感受到的就是跨平台的好处,我们组成员写的代码在我上面所说的几个OS上都跑起来了,性能我们还没有测试,[e08]。
                                                          我个人觉得通信服务器如果只在一个平台上跑,而且没有特别的要求的话,最好还是不要用。看过其源代码,还真是复杂,编译条件可选的也非常多。

                                                        外部的引用: 2 条

                                                        • 开源C/C++网络库比较:ACE、livevent和Boost – 伴随的blog
                                                        • 开源C/C++网络库比较:ACE、livevent和Boost – 源码巴士

                                                        给我留言

                                                        留言无头像?