Reactor 模式

Reactor

什麼是 Reactor 模式

wiki:「The reactor design pattern is an event handling pattern for handling service requests delivered concurrently by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to associated request handlers.」html

爲何會有 Reactor 呢

對於應用程序而言,CPU 的處理速度是遠遠快於 IO 的速度的。若是CPU爲了IO操做(例如從Socket讀取一段數據)而阻塞顯然是不划算的。好一點的方法是分爲多進程或者線程去進行處理,可是這樣會帶來一些進程切換的開銷,試想一個進程一個數據讀了500ms,期間進程切換到它3次,可是CPU卻什麼都不能幹,就這麼切換走了,是否是也不划算?java

這時先驅們找到了事件驅動,或者叫回調的方式,來完成這件事情。這種方式就是,應用業務向一箇中間人註冊一個回調(event handler),當IO就緒後,就這個中間人產生一個事件,並通知此handler進行處理。這種回調的方式,也體現了「好萊塢原則」(Hollywood principle)-「Don’t call us, we’ll call you」,在咱們熟悉的IoC中也有用到。看來軟件開發真是互通的!react

Reactor 應用場景

Reactor 核心是解決多請求問題。通常來講,Thread-Per-Connection 的應用場景併發量不是特別大,若是併發量過大,會致使線程資源瞬間耗盡,致使服務陷入阻塞,這個時候就須要 Reactor 模式來解決這個問題。Reactor 經過多路複用的思想大大減小線程資源的使用。設計模式

Reactor 結構

結構

上圖是 Reactor 模型,主要涉及的類:多線程

  • Initiation Dispatcher:EventHandler 的容器,用來註冊、移除 EventHandler 等;另外,它做爲 Reactor 模式的入口調用 Synchronous Event Demultiplexer 的 select 方法以阻塞等待事件的返回,當阻塞事件返回時,將事件發生的 Handle 分發到相應的 EvenHandler 處理。
  • Even Handler:定義了事件處理的方法。
  • Handle:即操做系統中的句柄,是對資源在操做系統層面上的一種抽象,它能夠是打開的文件、一個鏈接(Socket)、Timer等。
  • Synchronous Event Demultiplexer:使用一個事件循環 ,以阻止全部的資源。當能夠啓動一個同步操做上的資源不會阻塞,多路分解器發送資源到分發器。

Reactor 時序圖

時序圖

  1. 初始化 InitationDispatcher,並初始化一個Handle到EventHandler的Map。
  2. 註冊 EvenHandler 到 InitationDispatcher,每一個 EventHandler 包含對相應 Handle 的引用,從而創建Handle到EventHandler的映射(Map)。
  3. 調用 InitiationDispatcher 的 handle_events() 方法以啓動 Event Loop。在 Event Loop 中,調用 select()方法(Synchronous Event Demultiplexer)阻塞等待Event發生。
  4. 當某個或某些 Handle 的 Event 發生後,select() 方法返回,InitiationDispatcher 根據返回的Handle找到註冊的 EventHandler ,並回調該 EventHandler 的 handle_events() 方法。
  5. 在 EventHandler 的 handle_events() 方法中還能夠向 InitiationDispatcher 中註冊新的 Eventhandler,好比對 AcceptorEventHandler 來講,當有新的 client 鏈接時,它會產生新的 EventHandler 以處理新的鏈接,並註冊到 InitiationDispatcher 中。

Reactor 模式

單線程 Reactor 模式

reactor單線程

簡單來講,接收請求和處理請求是同一線程中處理。併發

對於一些小容量應用場景,可使用單線程模型。可是對於高負載、大併發或大數據量的應用場景卻不合適,主要緣由以下: ① 一個NIO線程同時處理成百上千的鏈路,性能上沒法支撐,即使NIO線程的CPU負荷達到100%,也沒法知足海量消息的讀取和發送; ② 當NIO線程負載太重以後,處理速度將變慢,這會致使大量客戶端鏈接超時,超時以後每每會進行重發,這更加劇了NIO線程的負載,最終會致使大量消息積壓和處理超時,成爲系統的性能瓶頸;ide

多線程 Reactor 模式

reactor多線程

簡單來講,接收請求和處理請求是不一樣線程中處理。模塊化

mainReactor 通常只有一個,主要負責接收客戶端的鏈接並將其傳遞給 subReactor。subReactor 通常會有多個,主要負責處理與客戶端的通訊。oop

注意:上圖使用了Thread Pool來處理耗時的業務邏輯,提升Reactor線程的I/O響應,不至於由於一些耗時的業務邏輯而延遲對後面I/O請求的處理。性能

Reactor 的優缺點

優勢:

  1. 大多數設計模式的共性:解耦、提高複用性、模塊化、可移植性、事件驅動、細力度的併發控制等。
  2. 更爲顯著的是對性能的提高,即不須要每一個 Client 對應一個線程,減小線程的使用。

缺點:

  1. 相比傳統的簡單模型,Reactor增長了必定的複雜性,於是有必定的門檻,而且不易於調試。
  2. Reactor模式須要底層的Synchronous Event Demultiplexer支持,好比Java中的Selector支持,操做系統的select系統調用支持,若是要本身實現Synchronous Event Demultiplexer可能不會有那麼高效。
  3. Reactor模式在IO讀寫數據時仍是在同一個線程中實現的,即便使用多個Reactor機制的狀況下,那些共享一個Reactor的Channel若是出現一個長時間的數據讀寫,會影響這個Reactor中其餘Channel的相應時間,好比在大文件傳輸時,IO操做就會影響其餘Client的相應時間,於是對這種操做,使用傳統的Thread-Per-Connection或許是一個更好的選擇,或則此時使用Proactor模式。

Reactor 代碼實現

預告

介紹與 Reactor 相關的 NIO 以及 Netty。

參考文獻

相關文章
相關標籤/搜索