學之者生,用之者死——ACE歷史與簡評

學之者生,用之者死——ACE歷史與簡評


陳碩 (giantchen_AT_gmail)

Blog.csdn.net/Solstice

2010 March 10

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 堆在那裏等別人來用。應該先有應用,再提煉出庫。而不是先造庫,然後尋求應用。

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/Solstice/archive/2010/03/10/5364096.aspx

發佈了50 篇原創文章 · 獲贊 7 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章