Reactor模式及在DSS中的體現
Reactor模式是處理并發I/O比較常見的一種模式,用于同步I/O,中心思想是將所有要處理的I/O事件注冊到一個中心I/O多路復用器上,同時主線程阻塞在多路復用器上;一旦有I/O事件到來或是準備就緒(區別在于多路復用器是邊沿觸發還是水平觸發),多路復用器返回并將相應I/O事件分發到對應的處理器中。
Reactor是一種事件驅動機制,和普通函數調用的不同之處在于:應用程序不是主動的調用某個API完成處理,而是恰恰相反,Reactor逆置了事件處理流程,應用程序需要提供相應的接口并注冊到Reactor上,如果相應的事件發生,Reactor將主動調用應用程序注冊的接口,這些接口又稱為“回調函數”。用“好萊塢原則”來形容Reactor再合適不過了:不要打電話給我們,我們會打電話通知你。
Reactor模式與Observer模式在某些方面極為相似:當一個主體發生改變時,所有依屬體都得到通知。不過,觀察者模式與單個事件源關聯,而反應器模式則與多個事件源關聯 。
模式框架
1) Handle
Handle代表操作系統管理的資源,包括:網絡鏈接,打開的文件,計時器,同步對象等等。Linux上是文件描述符,Windows上就是Socket或者Handle了,這里統一稱為“句柄集”;程序在指定的句柄上注冊關心的事件,比如I/O事件。
2) Event Demultiplexer
事件分離器,由操作系統提供,在linux上一般是select, poll, epoll等系統調用,在一個Handle集合上等待事件的發生。接受client連接,建立對應client的事件處理器(Event Handler),并向事件分發器(Reactor)注冊此事件處理器(Handler)。
3) Reactor(Initiation Dispatcher)
提供接口:注冊,刪除和派發Event Handler。Event Demultiplexer等待事件的發生,當檢測到新的事件,就把事件交給Initiation Dispatcher,它去回調Event Handler。
4) Event Handler
事件處理器,負責處理特定事件的處理函數。一般在基本的Handler基礎上還會有更進一步的層次劃分,用來抽象諸如decode,process和encoder這些過程。比如對Web Server而言,decode通常是HTTP請求的解析,process的過程會進一步涉及到Listner和Servlet的調用。為了簡化設計,Event Handler通常被設計成狀態機,按GoF的state pattern來實現。
5) Concrete Event Handler
繼承上面的類,實現鉤子方法。應用把Concrete Event Handler注冊到Reactor,等待被處理的事件。當事件發生,這些方法被回調。
事件處理流程
模式模型
應用場景舉例
場景:長途客車在路途上,有人上車有人下車,但是乘客總是希望能夠在客車上得到休息。
傳統做法:每隔一段時間(或每一個站),司機或售票員對每一個乘客詢問是否下車。
Reactor做法:汽車是乘客訪問的主體(Reactor),乘客上車后,到售票員(acceptor)處登記,之后乘客便可以休息睡覺去了,當到達乘客所要到達的目的地后,售票員將其喚醒即可。
1) 單線程模型
這是最簡單的單Reactor單線程模型。Reactor線程是個多面手,負責多路分離套接字,Accept新連接,并分派請求到處理器鏈中。該模型適用于處理器鏈中業務處理組件能快速完成的場景。不過這種單線程模型不能充分利用多核資源,所以實際使用的不多。
2) 多線程模型(單Reactor)
相比上一種模型,該模型在事件處理器(Handler)鏈部分采用了多線程(線程池),也是后端程序常用的模型。
3) 多線程模型(多Reactor)
這個模型比起第二種模型,它是將Reactor分成兩部分,mainReactor負責監聽并accept新連接,然后將建立的socket通過多路復用器(Acceptor)分派給subReactor。subReactor負責多路分離已連接的socket,讀寫網絡數據;業務處理功能,其交給worker線程池完成。通常,subReactor個數上可與CPU個數等同。
優缺點
優點
-
響應快,不必為單個同步時間所阻塞,雖然Reactor本身依然是同步的;
-
編程相對簡單,可以最大程度的避免復雜的多線程及同步問題,并且避免了多線程/進程的切換開銷;
-
可擴展性,可以方便的通過增加Reactor實例個數來充分利用CPU資源;
-
可復用性,Reactor框架本身與具體事件處理邏輯無關,具有很高的復用性;
缺點
-
應用受限制:Reactor模式只能應用在支持Handle的操作系統上。雖然可以使用多線程模擬Reactor,但因為同步控制和上下文切換的要求,這種實現效率低,與Reactor模式出發點相違背。
-
非搶占模式:在單線程的實現這種情況下,事件的處理必須不能使用阻塞的I/O,因此,如果存在長期操作,比如傳輸大量的數據。使用主動對象,效率可能更好,主動對象可以并發的處理這些任務。
-
難以調試:使用Reactor模式的應用程序可能會難以調試,因為程序運行的控制流會在框架和應用相關的處理器之間跳轉,不了解框架的應用程序開發人員難一跟著調試。
相關庫
ACE
ACE是一個大型的中間件產品,代碼20萬行左右,過于宏大,一堆的設計模式,架構了一層又一層,使用的時候,要根據情況,看從那一層來進行使用。支持跨平臺。
設計模式 :ACE主要應用了Reactor,Proactor等;
層次架構 :ACE底層是C風格的OS適配層,上一層基于C++的wrap類,再上一層是一些框架 (Accpetor,Connector,Reactor,Proactor等),最上一層是框架上服務;
可移植性 :ACE支持多種平臺,可移植性不存在問題,據說socket編程在linux下有不少bugs;
事件分派處理 :ACE主要是注冊handler類,當事件分派時,調用其handler的虛掛勾函數。實現 ACE_Handler/ACE_Svc_Handler/ACE_Event_handler等類的虛函數;
涉及范圍 :ACE包含了日志,IPC,線程池,共享內存,配置服務,遞歸鎖,定時器等;
線程調度 :ACE的Reactor是單線程調度,Proactor支持多線程調度;
發布方式 :ACE是開源免費的,不依賴于第三方庫,一般應用使用它時,以動態鏈接的方式發布動態庫;開發難度 :基于ACE開發應用,對程序員要求比較高,要用好它,必須非常了解其框架。在其框架下開發,往往new出一個對象,不知在什么地方釋放好。
Libevent
libevent是一個C語言寫的網絡庫,官方主要支持的是類linux操作系統,最新的版本添加了對windows的IOCP的支持。在跨平臺方面主要通過select模型來進行支持。
設計模式 :libevent為Reactor模式;
層次架構:livevent在不同的操作系統下,做了多路復用模型的抽象,可以選擇使用不同的模型,通過事件函數提供服務;
可移植性 :libevent主要支持linux平臺,freebsd平臺,其他平臺下通過select模型進行支持,效率不是太高;
事件分派處理 :libevent基于注冊的事件回調函數來實現事件分發;
涉及范圍 :libevent只提供了簡單的網絡API的封裝,線程池,內存池,遞歸鎖等均需要自己實現;
線程調度 :libevent的線程調度需要自己來注冊不同的事件句柄;
發布方式 :libevent為開源免費的,一般編譯為靜態庫進行使用;
開發難度 :基于libevent開發應用,相對容易,具體可以參考memcached這個開源的應用,里面使用了 libevent這個庫。
Libev
與 libevent 一樣,libev 系統也是基于事件循環的系統,它在 poll()、select() 等機制的本機實現的基礎上提供基于事件的循環。libev 實現的開銷更低,能夠實現更好的基準測試結果。
Reactor模式在DSS中的體現
Darwin流媒體服務器是由父進程及其fork出來的子進程構成的,子進程就是核心服務器。父進程的職責就是等待子進程退出。如果子進程出錯退出,則父進程就會fork一個新的子進程,從而保證視頻服務器繼續提供服務。核心服務器的作用是充當VOD(視頻點播)客戶端與服務器模塊之間的接口,VOD客戶端采用RTP和RTSP協議向服務器發送請求并接收響應,服務器模塊負責處理VOD客戶端的請求并向VOD客戶端發送數據包。
在DSS中,除主線程以外,還有有三種類型的線程:
-
TaskThread:TaskThread通過運行Task類型對象的Run方法來完成相應Task的處理。典型的Task類型是RTSPSession和RTPSession。TaskThread的個數是可配置的,缺省情況下TaskThread的個數與處理器的個數一致。等待被TaskThread調用并運行的Task放在隊列或者堆中。
-
EventThread:EventThread負責偵聽套接口事件,在DSS中,有兩種被偵聽的事件,分別是建立RTSP連接請求的到達和RTSP請求的到達。對于RTSP連接請求的事件,EventThread建立一個RTSP
Session,并啟動針對相應的socket的偵聽。對于RTSP請求的事件,EventThread把對應的RTSPSession類型的Task加入到TaskThread的隊列中,等待RTSP請求被處理。 -
IdleTaskThread:IdleTaskThread管理IdleTask類型對象的隊列,根據預先設定的定時器觸發IdleTask的調度。TCPListenerSocket就是一個IdleTask的派生類,當并發連接數達到設定的最大值時,會把派生自TCPListenerSocket的RTSPListenerSocket加入到IdleTaskThread管理的IdleTask隊列中,暫時停止對RTSP端口的偵聽,直到被設定好的定時器觸發。
下圖是Darwin Streaming Server核心架構的示意圖。在這個示意圖中有三種類型的要素,分別是線程,Task隊列或者堆,被偵聽的事件。
其中,事件線程(Event thread)是Event Demultiplexer(事件分離器),任務線程(Task threads)是Event Handler(事件處理器)。
這里的主線程(Main thread)就是Reactor模式中的Reactor(Initiation Dispatcher)。
原文鏈接:http://www.cnblogs.com/MikeZhang/archive/2012/08/16/ReactorPattern20120815.html