平時接觸的開源產品如Redis、ACE舱禽,事件模型都使用的Reactor模式炒刁;而同樣做事件處理的Proactor,由于操作系統(tǒng)的原因誊稚,相關的開源產品也少翔始;這里學習下其模型結構,重點對比下兩者的異同點里伯;Reactor 和 Proactor 是基于事件驅動城瞎,在網絡編程中經常用到兩種設計模式。
說到異步IO疾瓮,其實現在很難實現真正的異步脖镀,大部分情況下仍然需要阻塞在某個多路復用函數,比如select 或者 epoll 上爷贫,得到就緒描述符认然,然后調用注冊在相應描述符上的回調函數。這種方式是現在的反應堆設計的基本思路漫萄。我截取一段反應堆模型的圖給大家看看卷员。
事件循環(huán)阻塞查看描述符是否就緒,當就緒后返回可讀或可寫的描述符腾务,也有可能帶外數據或者出錯等情況毕骡。
因為 select 很多文章都介紹了,下面我就以 epoll 為例,貌似是2.4.6還是哪個版本以后加入的IO多路復用方式未巫。
epoll 較select 的一些優(yōu)點就不多說了窿撬,內核采用紅黑樹機制,大大提高了epoll 的性能叙凡。著名的 libevent Nginx等內部都采用這個機制劈伴。
反應器Reactor
在事件驅動的應用中,將一個或多個客戶的服務請求分離(demultiplex)和事件分發(fā)器 (dispatch)給應用程序握爷。
上下文
在事件驅動的應用中跛璧,同步地、有序地處理同時接收的多個服務請求新啼。
問題
在分布式系統(tǒng)尤其是服務器這一類事件驅動應用中追城,雖然這些請求最終會被序列化地處理,但是必須時刻準備著處理多個同時到來的服務請求燥撞。在實際應用中座柱,這些請求總是通過一個事件(如CONNECTOR、READ物舒、WRITE等)來表示的色洞。在有 序地處理這些服務請求之前,應用程序必須先分離和調度這些同時到達的事件茶鉴。為了有效地解決這個問題锋玲,我們需要做到以下4方面:
為了提高系統(tǒng)的可測量性和反應時間,應用程序不能長時間阻塞在某個事件源上而停止對其他事件的處理涵叮,這樣會嚴重降低對客戶端的響應度惭蹂。
為了提高吞吐量,任何沒有必要的上下文切換割粮、同步和CPU之間的數據移動都要避免盾碗。
引進新的服務或 改良已有的服務都要對既有的事件分離和調度機制帶來盡可能小的影響。
大量的應用程序代碼需要隱藏在復雜的多線程和同步機制之后舀瓢。
解決方案
在一個或多個事件源上等待事件的到來廷雅,例如,一個已經連接的Socket描述符就是一個事件源京髓。
將事件的分離和調度整合到處理它的服務中航缀,而將分離和調度機制從應用程序對特定事件的處理中分離開,
也就是說分離和調度機制與特定的應用程序無關堰怨。
具體來說芥玉,每個應用程序提供的每個服務都有一個獨立的事件處理器與之對應。
由 事件處理器處理來自事件源的特定類型的事件备图。每個事件處理器都事先注冊到Reactor管理器中灿巧。
Reactor管理器使用同步事件分離器在一個或多個事件源中等待事件的發(fā)生赶袄。
當事件發(fā)生后,同步事件分離器通知Reactor管理器抠藕,最后由Reactor管理器調度和該事件相關的事件處理器來完成請求的服務饿肺。
結構
在Reactor模式中,有5個關鍵的參與者盾似。
描述符(handle):
由操作系統(tǒng)提供敬辣,用于識別每一個事件,如Socket描述符零院、文 件描述符等购岗。在Linux中,它用一個整數來表示门粪。
事件可以來自外部,如來自客戶端 的連接請求烹困、數據等玄妈。事件也可以來自內部,如定時器事件髓梅。
同步事件分離器 (demultiplexer):
是一個函數拟蜻,用來等待一個或多個事件的發(fā)生。調用者會被阻 塞枯饿,直到分離器分離的描述符集上有事件發(fā)生酝锅。
Linux的select函數是一個經常被使 用的分離器。
事件處理器接口(event handler):
是由一個或多個模板函數組成的接口奢方。這些模板函數描述了和應用程序相關的對某個事件的操作搔扁。具體的事件處理器:是事件處理器接口的實現。它實現了應用程序提供的某個服務蟋字。每個具體的事件處理器總和一個描述符相關稿蹲。它使用描述符來識別事件、識別應用程序提供的服務鹊奖。
Reactor管理器(reactor):
定義了一些接口苛聘,用于應用程序控制事件調度,以及應用程序注冊忠聚、刪除事件處理器和相關的描述符设哗。
它是事件處理器的調度核心。Reactor管理器使用同步事件分離器來等待事件的發(fā)生两蟀。一旦事件發(fā)生网梢,Reactor管理器先是分離每個事件,然后調度事件處理器垫竞,最后調用相關的模板函 數來處理這個事件澎粟。
通過上述分析蛀序,我們注意到,是Reactor管理器而不是應用程序負責等待事件活烙、分離事件和調度事件徐裸。
實際上,Reactor管理器并沒有被具體的事件處理器調用啸盏,而是管理器調度具體的事件處理器重贺,由事件處理器對發(fā)生的事件做出處理。
這就是類似Hollywood原則的“反向控制”回懦。應用程序要做的僅僅是實現一個具體的事件處理器气笙,然后把它注冊到Reactor管理器中。
接下來的工作由管理 器來完成怯晕。
注意:這里提及的反應堆模型潜圃,實際上就是外國人設計的一個概念,將我們從面向過程編程轉換為一個面向對象編程的一個東西舟茶,我們可以簡單的認為谭期,直接操作IO模型,是一個面向過程的操作吧凉,而由一個反應堆來操作的隧出,是一個面向對象的操作,期間阀捅,面相對象操作會提高部分性能胀瞪。
Reactor包含如下角色:
1.Handle 句柄;用來標識socket連接或是打開文件饲鄙;
2.Synchronous Event Demultiplexer:同步事件多路分解器:由操作系統(tǒng)內核實現的一個函數凄诞;用于阻塞等待發(fā)生在句柄集合上的一個或多個事件;(如select/epoll忍级;)
3.Event Handler:事件處理接口
4.Concrete Event HandlerA:實現應用程序所提供的特定事件處理邏輯幔摸;
5.Reactor:反應器,定義一個接口颤练,實現以下功能:
1)供應用程序注冊和刪除關注的事件句柄既忆;
2)運行事件循環(huán);
3)有就緒事件到來時嗦玖,分發(fā)事件到之前注冊的回調函數上處理患雇;