Reactor 模型的實現

NIO 常用的編程模型是 Reactor,在 Doug Lea 的 Scalable IO in Java 的 PPT 中對其進行了介紹,文末有福利 :) ,Reactor 的特點是 I/O 多路複用和事件驅動,基本處理過程爲:

  • 處理程序聲明感興趣的 I/O 事件,這些事件表示在特定套接字上準備讀取的情況
  • 事件通知器等待事件
  • 一個事件發生並喚醒通知器,通知器調用適當的處理程序
  • 事件處理程序執行實際的讀取操作,並進行處理,然後重新聲明關注的 I/O 事件,並將控制權返回給調度程序

其中通知器,就是 Selector。Reactor模型主要有以下幾種版本:

  • 單線程Reactor,單線程處理器
  • 單線程Reactor,多線程處理器
  • 多線程主從Reactor,單線程處理器
  • 多線程主從Reactor,多線程處理器

單線程版本

單線程版本

核心代碼:

 public void run() {
    try { 
      if      (state == READING) read(); 
      else if (state == SENDING) send();
    } catch (IOException ex) { /* ... */ }
  }
  void read() throws IOException {
    socket.read(input);
    if (inputIsComplete()) {
       process(); 
       state = SENDING; 
       // Normally also do first write now
       sk.interestOps(SelectionKey.OP_WRITE);
    }
  }
  void send() throws IOException {
    socket.write(output);
    if (outputIsComplete()) sk.cancel();
  } 

單線程的特點是:只有一個 Reactor 線程,即只有一個 Selector 事件通知器,也就是說,字節的讀取 I/O 和後續的業務處理(process() 方法),均由 Reactor 線程來做,很顯然業務的處理影響後續事件的分發,所以引出多線程版本進行優化。

多線程版本

多線程版本

核心代碼:

static PooledExecutor pool = new PooledExecutor(...);
static final int PROCESSING = 3;
// ...
synchronized void read() { // ...
  socket.read(input);
  if (inputIsComplete()) {
    state = PROCESSING;
    pool.execute(new Processer());
  }
}
synchronized void processAndHandOff() {
  process();
  state = SENDING; // or rebind attachment
  sk.interest(SelectionKey.OP_WRITE);
}
class Processer implements Runnable {
  public void run() { processAndHandOff(); }
}

多線程版本的特點是:一個 Reactor 線程和多個處理線程,將業務處理(process 交給線程池)進行了分離,Reactor 線程,只關注事件分發和字節的發送和讀取(I/O)。注意,實際的發送和讀取還是由 Reactor 處理,那麼在高併發下,有可能連接來不及接收,繼續優化,採用主從 Reactor。

主從 Reactor

主從 Reactor

核心代碼:

Selector[] selectors; // also create threads
int next = 0;
class Acceptor { // ... 
  public synchronized void run() { ...
    Socket connection = serverSocket.accept();
    if (connection != null)
      new Handler(selectors[next], connection);
    if (++next == selectors.length) next = 0;
  }
}

主從 Reactor 特點是:使用一個 Selector 池,通常有一個 主Reactor 用於處理接收連接事件,多個 從Reactor 處理實際的 I/O,整體來看,分工合作,分而治之,非常高效。

在真正實現時,有些細節需要注意,完整代碼下載https://github.com/rmwheel/reactor
代碼有詳細註釋,看完絕對能理解 Reactor,其中包含對 Doug Lea 的 Scalable IO in Java 的 翻譯,歡迎 star :)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章