隨着網絡設計模式的興起,Reactor和Proactor事件處理模式應運而生。同步I/O模型通常用於實現Reactor模式,異步I/O模型則用於實現Proactor模式。
一、Reactor(反應堆/器)
Reactor 是這樣一種模式,它要求主線程(I/O處理單元,下同)只負責監聽文件描述上是否有事件發生,有的話就立即將該事件通知工作線程(邏輯單元,下同)。除此之外,主線程不做任何其他實質性的工作。讀寫數據,接受新的連接,以及處理客戶請求均在工作線程中完成。
使用同步I/O模型(以epoll_wait爲例)實現的Reactor模式的工作流程是:
1、主線程往epoll內核事件表中註冊socket上的讀就緒事件;
2、主線程調用epoll_wait等待socket上有數據可讀;
3、當socket上有數據可讀時,epoll_wait通知主線程。主線程則將socket可讀事件放入請求隊列;
4、睡眠在請求隊列上的某個工作線程被喚醒,它從socket讀取數據,並處理客戶請求,然後往epoll內核事件表中註冊該socket上的寫就緒事件。(註冊完後進入睡眠狀態,不佔用cpu資源)
5、主線程調用epoll_wait等待socket可寫。
6、當socket可寫時,epoll_wait通知主線程。主線程則將socket可寫事件放入請求隊列;
7、睡眠在請求隊列上的某一個工作線程被喚醒,它往socket上寫入服務器處理客戶請求的結果。
上圖工作線程從請求隊列中取出事件後,將根據事件的類型來決定如何處理它:對於可讀事件,執行讀數據和處理請求的操作;對於可寫事件,執行寫數據的操作。因此,上圖的Reactor模式中,沒有必要區分所謂的“讀工作線程”和“寫工作線程”。
二、Proactor(前攝器)
與Reactor模式不同,Proactor模式將所有I/O操作都交給主線程和內核來處理,工作線程僅僅負責業務邏輯。因此,Proactor更符合下圖所示的服務器框架
使用異步I/O模型(以aio_read和aio_write爲例)實現Proactor的工作流程是:
1、主線程調用aio_read函數向內核註冊socket上的讀完成事件,並告訴內核用戶讀緩衝區的位置,以及讀操作完成時如何通知應用程序(這裏以信號爲例);
2、主線程繼續處理其他邏輯;
3、當socket上的數據被讀入用戶緩衝區後,內核將嚮應用程序發送一個信號,以通知應用程序數據已經可用;
4、應用程序預先定義好的信號處理函數選擇一個工作線程來處理客戶請求,工作線程處理完客戶請求之後,調用aio_write函數向內核註冊socket上的寫完成事件,並告訴內核用戶寫緩衝區的位置,以及操作完成時如何通知應用程序;
5、主線程繼續處理其他邏輯;
6、當用戶緩衝區的數據被寫入socket之後,內核將嚮應用程序發送一個信號,以通知應用程序數據已經發送完畢;
7、應用程序預先定義好的信號處理函數選擇一個工作線程來做善後處理,比如決定是否關閉socket;
下圖總結了Proactor模式的工作流程。
在上圖中,連接socket上的讀寫事件是通過aio_read/aio_write向內核註冊的,因此內核將通過信號來嚮應用程序報告連接socket上的讀寫事件。所以,主線程中的epoll_wait調用僅能用來檢測監聽socket上的連接請求事件,而不能用來檢測連接socket上的讀寫事件。
三、Reactor和Proactor應用舉例
採用Reactor模式的開源庫
libevent: libevent 將定時事件,信號處理,I/O 事件結合在在一起,也就是說用戶同時在 Reactor 中註冊上述三類事件;
ACE:ACE支持反應器服務端編程框架,用於事件多路分離和分派的體系結構模式;
採用Proactor模式的開源庫
ACE:ACE支持前攝器服務端編程框架,前攝器模型簡化了異步的Web服務器開發難度及工作量;
Boost.asio:asio在Linux平臺下的實現基於epoll,但是epoll只支持reactor模式,ASIO通過封裝在epoll上實現了proactor。
四、Reactor和Proactor對比
併發模式 | reacotr | proactor |
---|---|---|
用戶工作量 | 用戶需進行數據接收、處理、發送等 | 用戶只需要進行數據處理,數據接收和發送均由主線程和內核完成 |
優點 | (1)Reactor實現相對簡單,對於耗時短的處理場景處理高效;(2)操作系統可以在多個事件源上等待,並且避免了多線程編程相關的性能開銷和編程複雜性;(3)事件的串行化對應用是透明的,可以順序的同步執行而不需要加鎖;(4)事務分離:將與應用無關的多路分解和分配機制和與應用相關的回調函數分離開來 | 性能更高,能夠處理耗時長的併發場景; |
缺點 | Reactor處理耗時長的操作會造成事件分發的阻塞,影響到後續事件的處理 | (1)Proactor實現邏輯複雜;(2)依賴操作系統對異步的支持,目前實現了純異步操作的操作系統少,實現優秀的如windows IOCP,但由於其windows系統用於服務器的侷限性,目前應用範圍較小;(3)而Unix/Linux系統對純異步的支持有限,應用事件驅動的主流還是通過select/epoll來實現 |
適用場景 | 同時接收多個服務請求,並且依次同步的處理它們的事件驅動程序 | 異步接收和同時處理多個服務請求的事件驅動程序 |
五、附
本文參考鏈接:
鏈接1:https://segmentfault.com/a/1190000002715832
鏈接2:Linux高性能服務器編程