模型:
反應器模式做法是:汽車是乘客訪問的主體(Reactor),乘客上車后,到售票員(acceptor)處登記,之后乘客便可以休息睡覺去了,當到達乘客所要到達的目的地后,售票員將其喚醒即可。
反應器模式與觀察者模式在某些方面極為相似:當一個主體發生改變時,所有依屬體都得到通知。不過,觀察者模式與單個事件源關聯,而反應器模式則與多個事件源關聯。
JDK中模式原型復現:
NIO有一個主要的類Selector,這個類似一個觀察者,只要我們把需要探知的socketchannel告訴Selector,我們接着做別的事情,當有事件發生時,他會通知我們,傳回一組SelectionKey,我們讀取這些Key,就會獲得我們剛剛注冊過的socketchannel,然后,我們從這個Channel中讀取數據,接着我們可以處理這些數據。
首先,僅僅是建立連接(socket),就占用了服務器的線程資源。如果客戶端還沒有發出相應的數據請求,那么服務器就要一直等待他們的數據流過來,然后再進行讀取,如此往復 … … 一直都blocking。服務器處在一個高負荷狀態中。
NIO出來之后,進入改革開放時期,有了這么幾個角色,ServerSocketChannel,SelectionKey,Selector。
這幾個角色都是做什么用的呢?需要了解一下reactor模式(反應堆模式)。作為服務端,如果什么操作都要依賴於客戶端,很多操作都阻塞,如上面的代碼片段所示。reactor模式提供了一種很好的事件處理機制,以分離事件處理對象與事件之間的耦合。如下圖示(詳細請看參考資料(1)):
說明:
Acceptor就是我們Server端的主要任務消化者;
Initiation Dispatcher是事件(Event)的分發者;
HTTP Handler是具體操作人。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
首先,在Initiation Dispatcher中注冊一個acceptor
(1:register Acceptor),這個Acceptor是跟事件綁定的,它僅僅關心某種事件(event)。Initiation Dispatcher不斷地循環獲取請求過來的事件
(2:handle events),如果發現有對應Acceptor關心的事件
(3:connect),通知Acceptor有事件發生
(4:new connection)。 Acceptor針對此事件進行處理,創建了新的HTTP Handler
(5:create handler))一輪事件獲取和分發完成。
那么handler是不是就抓住這個connection不放,然后一直苦苦等待數據流的到來呢?
不是的,它也是將自己和自己關心的事件注冊到Initiation Dispatcher。如果Initiation Dispatcher在handle Events時發現了它關心的事件,那么就會交由它去進行相應處理。如下圖示,在連接完成后,browser提交的get請求,handler的處理過程:
這里面尤其要注意到,2:read ready,之后才read request,也就是說,handler在dispatcher中注冊了自己關心的事件(READ),然后在寫的時候,也是類似情況。
以上的過程就實現了非阻塞的處理方式,客戶端的連接可以非阻塞(這是意思是,acceptor不必一直苦苦等候),然后對客戶端過來的request內容,也是非阻塞(這里是不必苦苦等待其數據的到來),都是不必一直眼巴巴的看着那個連接,那些數據,而是如果有我關心的事件了,我再進行處理,期間完全相信Initiation Dispatcher就行了。
這里有一點要注意,就是現在的reactor模式都是建立在操作系統的基礎上實現的,不同的操作系統有不同的實現方式。而且都不支持多線程(針對Initiation Dispatcher而言)。