為何 Boost 的 Asio 要使用 Proactor 模式實現?

Linux下高性能的網路庫中大多使用的Reactor 模式去實現,Boost Asio在Linux下用epoll和select去模擬proactor模式,影響了它的效率和實現複雜度,

看陳碩的自己的Linux下Reactor網路庫和ASIO的性能對比,大概比asio性能(吞吐量)高1/5.既然Linux下網路庫用Reactor性能才高,為什麼Boost ASIO Linux下要用模擬的Proactor模式?

或者說為什麼ASIO不在win和linux都用Reactor模式?這樣的選擇是不是可以性能更好?和更加適應市場?伺服器端畢竟大量都是Linux.


Windows 下很難實現高效可伸縮的 Reactor。首先,Win32 API 里 WaitForMultipleObjects 只能同時等待 64 個 handle (MAXIMUM_WAIT_OBJECTS);其次 WinSock 的 select() 實現又很 buggy,特別是在錯誤處理方面有很多奇葩行為(具體見各種跨平台網路庫代碼中對此的注釋);最後,Windows Vista 新增的 WSAPoll() 函數與 POSIX 的 poll() 又不盡兼容( http://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/ )。

Windows 有自己的一套高效非同步IO模型(幾乎等同於Proactor),同時支持文件IO和網路IO;但 Linux 只有高效的網路同步IO(epoll 之類的 io multiplexing 是同步的Reactor,且不支持磁碟文件),二者的高效IO編程模型從根本上不兼容(Windows 可以把網路事件發到 GUI 線程的事件隊列中,有點類似 Reactor,但是似乎一個進程只能有一個 GUI 線程,因此在多核系統上其伸縮性受限)。

因此,ASIO 要想高效且跨平台,只能用 Proactor 模型了。不可避免地會在 Linux 上損失一點兒效率。


Proactor的編程模型相比Reactor要更自然一些,而且在操作系統有支持的情況下,能獲得更好的性能.所以一個通用網路庫,選擇Proactor作為介面語義並不奇怪.只是他們沒想到,Linux社區對於填上aio的坑並沒太大的動力(或則足夠的實力?),這基於epoll的模擬Proactor實現一用就是數十年~


unix的遺毒,能用就用,不思完美,不過它勝過了追求完美的Lisp,讓人無語,也許滿足當下,追求近利是必然的選擇吧,不過商用的Windows後發先至,弄了一個IO完成埠,讓人感嘆。。。


這個對比並不能說明性能差距的原因之所在是因為asio模擬proactor啊。其實如果asio不模擬proactor,很大一部分的工作不也得自己做嗎?比如把接收的數據從kernel space拷貝到user space,asio幫你做好了,開銷是一個queue,個人認為這個開銷很小。

另外不同的網路庫,由於設計的不同,會有自己的一些長處和短處,比如asio的線程沒有專門的I/O線程從而可以支持long-running task,但加重了cpu保證cache一致性的開銷。有些網路庫雖然沒有這種開銷,但如果有long-running task會阻塞I/O,需要自己開線程處理。再比如asio沒有實現read write buffer,跑分的代碼怎麼實現的這個buffer也是對於性能而言的一個可變因素。

用一些簡單的test比如echo去跑分做橫向比較意義有限,實際的項目肯定是很複雜的,可不是單單發送數據這麼簡單,最終還是要根據實際情況來定。

至於為什麼asio要選擇proactor,個人認為還是設計的原因:儘可能的照顧到了所有人的需求,不會出現因為某種特殊需求導致的性能問題。但也正是因為如此,庫比較底層,一些本該有的功能得自己搓,而且這些特殊需求估計99.9%的項目都不會遇到。


推薦閱讀:

使用Erlang實現http server是否更有優勢?
為什麼目前web伺服器一般用Linux操作系統而不是windows?
Node伺服器是如何處理大量請求的?
鬥魚是怎麼做到十幾萬人超清直播的,帶寬夠嗎?
docker和virtualenv有什麼區別?

TAG:編程語言 | 伺服器 | C | Reactor與Proactor |