兩種高性能 I/O 設計模式 Reactor 和 Proactor

Reactor 和 Proactor 是基於事件驅動,在網絡編程中常常用到兩種設計模式。html

曾經在一個項目中用到了網絡庫 libevent,也學習了一段時間,其內部實現所用到的就是 Reactor,所知道的還有 ACE;Proactor 模式的庫有 Boost.Asio,ACE,暫時沒有用過。但我也翻閱了一些文檔,理解了它的實現方法。下面是我在學習這兩種設計模式過程的筆記。react

Reactor

Reactor,即反應堆。Reactor 的通常工做過程是首先在 Reactor 中註冊(Reactor)感興趣事件,並在註冊時候指定某個已定義的回調函數(callback);當客戶端發送請求時,在 Reactor 中會觸發剛纔註冊的事件,並調用對應的處理函數。在這一個處理回調函數中,通常會有數據接收、處理、回覆請求等操做。web

reactor_pattern

libevent 採用的就是 Reactor 的設計思想。其 Reactor 的中心思想是衆所周知的 I/O 多路複用:select,poll,epoll,kqueue 等.libevent 精彩的將定時事件,信號處理,I/O 事件結合在在一塊兒,也就是說用戶同時在 Reactor 中註冊上述三類事件。遺憾的是,libevent 不支持多線程,也就是說它同步處理請求,致使不能處理大量的請求;這樣並非說 Reactor 實現的網絡庫都不支持多線程,而是 libevent 自己的緣由,咱們也能夠經過修改讓 ilbevent 支持多線程,併發處理多個請求。編程

下面是 libevent 的一段代碼,大概可以說明 Reactor 工做模式:設計模式

/*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_uml

和時序圖:服務器

proactor_timing_diagram

注:這兩幅美豔的圖片來自 Proactor.doc,下面會提到.網絡

Proactor 的實現主要有三個部分:異步操做處理器,Proactor 和 事件處理函數。其中:多線程

- 異步操做處理器,很依賴操做系統的異步處理機制,如若操做系統沒有實現,咱們能夠自行模擬,即開專門的數據讀寫線程,數據讀寫完畢觸發相應的時間(若是有註冊的話);
- Proactor,會接收異步操做的提醒,調用相應的事件處理函數,它有本身的 event loop;
- 事件處理函數,事件觸發,執行操做;

曾經看過 Proactor.doc,做者是 Douglas C. Schmidt,你能夠在這裏閱讀此文檔。裏面的關於 Proactor 的講解很精彩,部分摘抄和本身的理解以下:當鏈接 web 服務器時:併發

proactor_web_connect

  • 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 請求事後,會處理數據:

proactor_web_service

  • 瀏覽器發送(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

http://daoluan.net

相關文章
相關標籤/搜索