高性能I/O设计模式Reactor和Proactor
昨天購買了《程序員》雜志?2007.4期,第一時間去翻閱了一遍,其中有一篇《兩種高性能I/O設計模式的比較》令人眼睛一亮,這是一篇譯文,偶最近在一直想認真看看這方面的文章很久了。
文章主要是講到了系統I/O方式可分為阻塞,非阻塞同步和非阻塞異步三類,三種方式中,非阻塞異步模式的擴展性和性能最好。主要是講了兩種IO多路復用模式:Reactor和Proactor,并對它們進行了比較。
文章還介紹了為Reactor和Proactor模式構建一個通用的,統一的對外接口并是一個完全可移植的開發框架選擇方案:TProactor(ACE compatible Proactor) :http://www.terabit.com.au/solutions.php。因為Linux對aio支持的不完整,所以ACE_Proactor框架在linux上的表現很差,大部分在windows上執行正常的代碼,在Linux則運行異常,甚至不能編譯通過。這個問題一直困擾著很大多數ACE的用戶,現在好了,有一個TProactor幫助解決了在Linux不完整支持AIO的條件下,正常使用(至少是看起來正常)ACE_Proactor。
文章主要摘要:
---------->
兩種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二者的差異,以讀操作為例(類操作類似)。
?在Reactor中實現讀:
?- 注冊讀就緒事件和相應的事件處理器
?- 事件分離器等待事件
?- 事件到來,激活分離器,分離器調用事件對應的處理器。
?- 事件處理器完成實際的讀操作,處理讀到的數據,注冊新的事件,然后返還控制權。
?與如下Proactor(真異步)中的讀過程比較:
?- 處理器發起異步讀操作(注意:操作系統必須支持異步IO)。在這種情況下,處理器無視IO就緒事件,它關注的是完成事件。
?- 事件分離器等待操作完成事件
?- 在分離器等待過程中,操作系統利用并行的內核線程執行實際的讀操作,并將結果數據存入用戶自定義緩沖區,最后通知事件分離器讀操作完成。
?- 事件分離器呼喚處理器。
?- 事件處理器處理用戶自定義緩沖區中的數據,然后啟動一個新的異步操作,并將控制權返回事件分離器。
?
實踐現狀?
?由Douglas Schmidt等人開發的開源C++開發框架ACE,提供了大量與平臺無關,支持并發的底層類(線程,互斥量等),且在高抽象層次上,提供了兩組不同的類--ACE Reactor和ACE Proactor的實現。不過,雖然二者都與平臺無關,提供的接口卻各異。
?ACE Proactor在windows平臺上具有更為優異的性能表現,因為windows在操作系統提供了高效的異步API支持(見http://msdn2.microsoft.com/en-us/library/aa365198.aspx)。
?然而,并非所有的操作系統都在系統級大力支持異步。像很多Unix系統就沒做到。因此,在Unix上,選擇ACE Reactor解決方案可能更好。但這樣一來,為了獲得最好的性能,網絡應用的開發人員必須為不同的操作系統維護多份代碼:windows上以ACE Proactor為基礎,而Unix系統上則采用ACE Reactor解決方案。
?
改進方案
? 在這部分,我們將嘗試應對為Proactor和Reactor模式建立可移植框架的挑戰。在改進方案中,我們將Reactor原來位于事件處理器內的read/write操作移至分離器(不妨將這個思路稱為“模擬異步”),以此尋求將Reactor多路同步IO轉化為模擬異步IO。以讀操作為例子,改進過程如下:
? - 注冊讀就緒事件及其處理器,并為分離器提供數據緩沖區地址,需要讀取數據量等信息。
? - 分離器等待事件(如在select()上等待)
? - 事件到來,激活分離器。分離器執行一個非阻塞讀操作(它有完成這個操作所需的全部信息),最后調用對應處理器。
? - 事件處理器處理用戶自定義緩沖區的數據,注冊新的事件(當然同樣要給出數據緩沖區地址,需要讀取的數據量等信息),最后將控制權返還分離器。
? 如我們所見,通過對多路IO模式功能結構的改造,可將Reactor轉化為Proactor模式。改造前后,模型實際完成的工作量沒有增加,只不過參與者間對工作職責稍加調換。沒有工作量的改變,自然不會造成性能的削弱。對如下各步驟的比較,可以證明工作量的恒定:
? 標準/典型的Reactor:
? - 步驟1:等待事件到來(Reactor負責)
? - 步驟2:將讀就緒事件分發給用戶定義的處理器(Reactor負責)
? - 步驟3:讀數據(用戶處理器負責)
? - 步驟4:處理數據(用戶處理器負責)
? 改進實現的模擬Proactor:
? - 步驟1:等待事件到來(Proactor負責)
? - 步驟2:得到讀就緒事件,執行讀數據(現在由Proactor負責)
? - 步驟3:將讀完成事件分發給用戶處理器(Proactor負責)
? - 步驟4:處理數據(用戶處理器負責)?
?
? 對于不提供異步IO API的操作系統來說,這種辦法可以隱藏socket API的交互細節,從而對外暴露一個完整的異步接口。借此,我們就可以進一步構建完全可移植的,平臺無關的,有通用對外接口的解決方案。
? ?
?上述方案已經由Terabit P/L公司(http://www.terabit.com.au/)實現為TProactor。它有兩個版本:C++和JAVA的。C++版本采用ACE跨平臺底層類開發,為所有平臺提供了通用統一的主動式異步接口。
? Boost.Asio庫,也是采取了類似的這種方案來實現統一的IO異步接口。
<-----------
最近在項目中使用了Boost.Asio類庫,其就是以Proactor這種設計模式來實現,參見:Proactor(The Boost.Asio library is based on the Proactor pattern. This design note outlines the advantages and disadvantages of this approach.),其設計文檔鏈接:http://asio.sourceforge.net/boost_asio_0_3_7/libs/asio/doc/design/index.html
First, let us examine how the Proactor design pattern is implemented in asio, without reference to platform-specific details.
Proactor design pattern (adapted from [1])
當然這兩I/O設計模式,也在ACE中被大量應用,這在ACE的相關書籍中都有介紹,其中在“ACE開發者”網站中有很多不錯的介紹文章。
如:ACE技術論文集-第8章 前攝器(Proactor):用于為異步事件多路分離和分派處理器的對象行為模式
ACE技術論文集-第7章 ACE反應堆(Reactor)的設計和使用:用于事件多路分離的面向對象構架
ACE程序員教程-第6章 反應堆(Reactor):用于事件多路分離和分派的體系結構模式
ACE應用-第2章 JAWS:高性能Web服務器構架
?
Proactor模式在單CPU單核系統應用中有著無可比擬的優勢,現在面臨的問題是:在多CPU多核的系統中,它如何更好地應用多線程的優勢呢???這是很值思考和實踐的,也許會產生另外一種設計模式來適應發展的需要啦。
總結
以上是生活随笔為你收集整理的高性能I/O设计模式Reactor和Proactor的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ACE中的Proactor和Reacto
- 下一篇: ACE_Select_Reactor 一