Doug Lea 在 Scalable IO in Java 的 PPT 中描述了 Reactor 編程模型的思想,大部分 NIO 框架和一些中間件的 NIO 編程都與它同樣或是它的變體。本文結合 PPT 按照本身的理解整理而來,最終編寫了一個簡單的 NIO 回顯服務。java
Reactor 之因此高效是由於採用了分而治之和事件驅動的設計。大部分網絡服務像 Web 服務器、分佈式對象的通訊等大多數具備相同的基本處理流程:react
傳統的服務器設計是:一鏈接一處理線程,也就是咱們常說的 BIO 編程。BIO 在讀取和發送時是阻塞的,在請求的整個生命週期內,無論有沒有數據可讀或待發送,都綁定和佔用了這個處理線程。git
傳統模型中,線程的處理單元是一次完整的請求,爲了把線程解放出來,Reactor 對這個處理單元進行了分解。github
Reactor 模式將處理過程分爲多個小任務,每一個任務執行一個非阻塞的操做,任務一般由一個 IO 事件觸發執行。這種機制在 java.nio 中提供了支持:編程
BIO 線程是以 read->decode->process->encode->send 的順序串行處理,NIO 將其分紅了三個執行單元:讀取、業務處理和發送,處理過程以下:設計模式
能夠看出一個明顯的區別就是,一次請求的處理過程是由多個不一樣的線程完成的,感受和指令的串行執行和並行執行有點相似。數組
分而治之的關鍵在於非阻塞,這樣就能充分利用線程,壓榨 CPU,提升系統的吞吐能力。服務器
一般比其餘模型更高效,它使用的資源更少,不用針對每一個請求啓用一條線程,減小了上下文切換,減小阻塞。但任務調度可能會慢,必須手動將事件和處理動做綁定。網絡
一般編程比較困難,它必須爲服務設計多個邏輯狀態,以便跟蹤和中斷恢復,這也是在非阻塞編程中有大量狀態機運用的緣由。多線程
NIO 中總共設計了 4 種事件,每一個事件發生都會調度關聯的任務,分別是:
java.nio 對事件驅動也提供了支持:
Reactor 是一種設計模式,wikipedia 對其定義以下:
Reactor 是一個或多個輸入事件的處理模式,用於處理併發傳遞給服務處理程序的服務請求。服務處理程序判斷傳入請求發生的事件,並將它們同步的分派給關聯的請求處理程序。
Reactor 模式按照職責不一樣,一般能夠把線程分爲 Reactor 線程、IO 線程和業務線程:
PPT 中介紹了三種版本的實現。
單線程版本就是用一個線程完成事件的通知、實際的 I/O 操做和業務處理。Reactor 做用就是要迅速的觸發 Handler ,顯然 Handler 處理的過程會致使 Reactor 變慢,此時能夠將 非 IO 操做從 Reactor 線程中分離。
多線程版本將業務處理和 I/O 操做進行分離,Reactor 線程只關注事件分發和實際的 IO 操做,業務處理如協議的編解碼都分配給線程池處理。可能會有這樣的狀況發生,業務處理很快,大部分的 Reactor 線程都在處理 IO,致使 CPU 閒置,下降了響應速度。
主從 Reactor 版本設計了一個 主Reactor 用於處理鏈接接收事件,多個 從Reactor 處理實際的 I/O,分工合做,匹配 CPU 和 IO 速率。
以上的三個版本中,Reactor 線程都參與了 IO 操做,有一種變體是把實際的 IO 操做也轉移到線程池線程中,這樣從數據的讀取到業務的處理都是由一個線程完成,能夠避免鎖的競爭。
知易行難,爲了更好的理解,這裏對 PPT 中介紹的3個版本實現了一個簡單的回顯服務,內部有比較詳細的代碼註釋。