10-高併發-Reactor-反應器模式-

一、總體來說,反應器模式類似事件驅動模式。

  • 在事件驅動模式中,當有事件觸發時,事件源會將事件dispatch分發到Handler處理器進行事件處理。
  • 反應器模式中的反應器角色,類似於事件驅動模式中的dispatch事件分發器角色。

二、反應器模式的重要組件:

  • Reactor反應器:負責查詢IO事件,當檢測到一個IO事件,將其發送給響應的Handler處理器。
  • Handler處理器:與IO事件(或者選擇鍵)綁定,負責IO事件的處理。完成真正的連接建立、通道讀取、業務處理、寫出結果。
  • SelectionKey選擇鍵的幾個重要方法:
  • void attach(Object o)---爲SelectionKey添加附件。
  • Object attachment()---獲取SelectionKey的附件。

三、單線程反應器實例

順序:

  1. 創建反應器。(打開選擇器,監聽端口)
  2. selector註冊serverSocket---ACCEPT(得到selectionKey-事件集)
  3. selection附件:Accpt處理器。
  4. 運行反應器,選擇器獲取事件。
  5. 分發事件。
  6. 獲取事件集對應的附件,運行Accpt處理器。
  7. Accpt處理器:接收新連接
  8. Accpt處理器:爲新連接創建IOHandler
  9. Accpt處理器:爲新連接創建IOHandler  ---- (1)新的SocketChannel傳輸通道註冊,同一個選擇器中,保證是一個線程處理。
  10. Accpt處理器:爲新連接創建IOHandler  ---- (2)將IOhandler自身作爲附件,加入選擇器中。
  11. Accpt處理器:爲新連接創建IOHandler  ---- (3)註冊事件:讀&寫。
class Reactor implements Runnable {
            Selector selector;
            ServerSocketChannel serverSocket;
            EchoServer Reactor() throws IOException {
              //....省略:打開選擇器、serverSocket連接監聽通道
              //註冊serverSocket的accept事件---註冊後才能被KEY選中---註冊事件會被選擇
              SelectionKey sk =serverSocket.register(selector, SelectionKey.OP_ACCEPT);
              //將新連接處理器作爲附件,綁定到sk選擇鍵
              //爲事件:新建連接-綁定處理器
              sk.attach(new AcceptorHandler());
            }

            public void run() {
              //選擇器輪詢
              try {
                  while (! Thread.interrupted()) {
                      //獲取IO事件
                      selector.select();
                      Set selected = selector.selectedKeys();
                      Iterator it = selected.iterator();
                      while (it.hasNext()) {
                      //反應器負責dispatch收到的事件
                          SelectionKey sk=it.next();
                          //分發
                          dispatch(sk);
                      }
                      selected.clear();
                  }
              } catch (IOException ex) { ex.printStackTrace(); }
            }
            //反應器的分發方法
            void dispatch(SelectionKey k) {
              //獲取處理器
              Runnable handler = (Runnable) (k.attachment());
              //調用之前綁定到選擇鍵的handler處理器對象
              if (handler ! = null) {
                  handler.run();
              }
          }
          // 新連接處理器
          class AcceptorHandler implements Runnable {
              public void run() {
                  //接受新連接
                  //需要爲新連接,創建一個輸入輸出的handler處理器-IOHandler
              }
          }
          //….
        }


//一、將新的SocketChannel傳輸通道,註冊到反應器Reactor的同一個選擇器中,保證是一個線程處理。
//二、將IOhandler自身作爲附件,加入選擇鍵中。

class IOHandler implements Runnable {
            final SocketChannel channel;
            final SelectionKey sk;
            IOHandler (Selector selector, SocketChannel c) throws IOException {
              channel = c;
              c.configureBlocking(false);
              //僅僅取得選擇鍵,稍候設置感興趣的IO事件
              sk = channel.register(selector, 0);
              //將Handler處理器作爲選擇鍵的附件
              sk.attach(this);
              //註冊讀寫就緒事件
              sk.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
          }
          public void run()  {
          //...處理輸入和輸出
          }
        }

單線程反應器的缺點:某個Handler阻塞時,會導致其他Handler得不到執行。

四、多線程Reactor反應器模式

總體思路:

  • 將負責輸入輸出處理的Handler的執行,放入獨立線程池。
  • 反應器拆分子反應器,每個子反應器負責一個選擇器,引入多個選擇器。
  • 本質上-就是引入多個選擇器,分發,處理。
        //....反應器
        class MultiThreadEchoServerReactor {
            ServerSocketChannelserverSocket;
            AtomicInteger next = new AtomicInteger(0);
            //選擇器集合,引入多個選擇器
            Selector[] selectors = new Selector[2];
            //引入多個子反應器
            SubReactor[] subReactors = null;
            MultiThreadEchoServer Reactor() throws IOException {
              //初始化多個選擇器
              selectors[0] = Selector.open();
              selectors[1] = Selector.open();
              serverSocket = ServerSocketChannel.open();
              InetSocketAddress address =
                    new InetSocketAddress(NioDemoConfig.SOCKET_SERVER_IP,
              NioDemoConfig.SOCKET_SERVER_PORT);
              serverSocket.socket().bind(address);
              //非阻塞
              serverSocket.configureBlocking(false);
              //第一個選擇器,負責監控新連接事件
              SelectionKeysk =
                    serverSocket.register(selectors[0], SelectionKey.OP_ACCEPT);
              //綁定Handler:attach新連接監控handler處理器到SelectionKey(選擇鍵)
              sk.attach(new AcceptorHandler());
              //第一個子反應器,一子反應器負責一個選擇器
              SubReactor subReactor1 = new SubReactor(selectors[0]);
              //第二個子反應器,一子反應器負責一個選擇器
              SubReactor subReactor2 = new SubReactor(selectors[1]);
              subReactors = new SubReactor[]{subReactor1, subReactor2};
          }

          private void startService() {
              // 一子反應器對應一個線程
              new Thread(subReactors[0]).start();
              new Thread(subReactors[1]).start();
          }

          //子反應器
          class SubReactor implements Runnable {
              //每個線程負責一個選擇器的查詢和選擇
              final Selector selector;
              public SubReactor(Selector selector) {
                  this.selector = selector;
              }
              public void run() {
                  try {
                    while (! Thread.interrupted()) {
                        selector.select();
                        Set<SelectionKey>keySet = selector.selectedKeys();
                        Iterator<SelectionKey> it = keySet.iterator();
                        while (it.hasNext()) {
                            //反應器負責dispatch收到的事件
                            SelectionKeysk = it.next();
                             dispatch(sk);
                        }
                    keySet.clear();
                    }
                  } catch (IOException ex) {
                    ex.printStackTrace();
                    }
              }
              void dispatch(SelectionKeysk) {
                  Runnable handler = (Runnable) sk.attachment();
                  //調用之前attach綁定到選擇鍵的handler處理器對象
                  if (handler ! = null) {
                    handler.run();
                  }
              }
          }
            // Handler:新連接處理器
            class AcceptorHandler implements Runnable {
                public void run() {
                  try {
                      SocketChannel channel = serverSocket.accept();
                      if (channel ! = null)
                          new MultiThreadEchoHandler(selectors[next.get()], channel);
                  } catch (IOException e) {
                      e.printStackTrace();
                    }
                  if (next.incrementAndGet() == selectors.length) {
                      next.set(0);
                  }
                }
            }
            public static void main(String[] args) throws IOException {
                MultiThreadEchoServerReactor server =
                          new MultiThreadEchoServerReactor();
                server.startService();
            }
        }

 

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