Java NIO 反应器(Reactor)模式

来源:互联网 发布:软件规格需求说明书 编辑:程序博客网 时间:2024/05/17 09:17

一、概述

Java NIO非堵塞技术实际是采取反应器模式,或者说是观察者(observer)模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。

同步和异步区别     : 有无通知(是否轮询)
堵塞和非堵塞区别  : 操作结果是否等待(是否马上又返回值),只是设计方式的不同。

NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的SocketChannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的SocketChannel,然后,我们从这个Channel中读取数据,接着我们可以处理这些数据。

反应器模式与观察者模式在某些方面极为相似:当一个主体发生改变时,所有依属体都得到通知。不过,观察者模式与单个事件源关联,而反应器模式则与多个事件源关联 。


二、一般模型

我们想象以下情形:长途客车在路途上,有人上车有人下车,但是乘客总是希望能够在客车上得到休息。

传统的做法是:每隔一段时间(或每一个站),司机或售票员对每一个乘客询问是否下车。

反应器模式做法是:汽车是乘客访问的主体(Reactor),乘客上车后,到售票员(acceptor)处登记,之后乘客便可以休息睡觉去了,当到达乘客所要到达的目的地后,售票员将其唤醒即可。


三、代码讲解

 

Java代码  收藏代码
  1. package com.ljn.reactor;  
  2.   
  3. import java.io.IOException;  
  4. import java.net.InetSocketAddress;  
  5. import java.nio.channels.SelectionKey;  
  6. import java.nio.channels.Selector;  
  7. import java.nio.channels.ServerSocketChannel;  
  8. import java.nio.channels.SocketChannel;  
  9. import java.util.Iterator;  
  10. import java.util.Set;  
  11.   
  12. /* 
  13.  
  14. 单线程的实现 
  15. Server端用一个Selector利用一个线程(在main方法里面start)来响应所有请求 
  16. 1.当ACCEPT事件就绪,Acceptor被选中,执行它的run方法:创建一个Handler(例如为handlerA),并将Handler的interestOps初始为READ 
  17. 2.当READ事件就绪,handlerA被选中,执行它的run方法:它根据自身的当前状态,来执行读或写操作 
  18. 因此,每一个Client连接过来,Server就创建一个Handler,但都所有操作都在一个线程里面 
  19.  
  20. Selection Key   Channel                 Handler     Interested Operation 
  21. ------------------------------------------------------------------------ 
  22. SelectionKey 0  ServerSocketChannel     Acceptor    Accept 
  23. SelectionKey 1  SocketChannel 1         Handler 1   Read and Write 
  24. SelectionKey 2  SocketChannel 2         Handler 2   Read and Write 
  25. SelectionKey 3  SocketChannel 3         Handler 3   Read and Write 
  26.  
  27. 如果采用多个selector,那就是所谓的“Multiple Reactor Threads”,大体思路如下: 
  28.  
  29. Selector[] selectors; // also create threads 
  30. int next = 0; 
  31. class Acceptor { // ...  
  32.      public synchronized void run() { ... 
  33.          Socket connection = serverSocket.accept(); 
  34.          if (connection != null) 
  35.              new Handler(selectors[next], connection); 
  36.          if (++next == selectors.length) next = 0; 
  37.      } 
  38. } 
  39.  
  40.  */  
  41. public class Reactor implements Runnable {  
  42.    
  43.     final Selector selector;  
  44.     final ServerSocketChannel serverSocketChannel;  
  45.     final boolean isWithThreadPool;  
  46.    
  47.     /*Reactor的主要工作: 
  48.      * 1.给ServerSocketChannel设置一个Acceptor,接收请求 
  49.      * 2.给每一个一个SocketChannel(代表一个Client)关联一个Handler 
  50.      * 要注意其实Acceptor也是一个Handler(只是与它关联的channel是ServerSocketChannel而不是SocketChannel) 
  51.      */  
  52.     Reactor(int port, boolean isWithThreadPool) throws IOException {  
  53.    
  54.         this.isWithThreadPool = isWithThreadPool;  
  55.         selector = Selector.open();  
  56.         serverSocketChannel = ServerSocketChannel.open();  
  57.         serverSocketChannel.socket().bind(new InetSocketAddress(port));  
  58.         serverSocketChannel.configureBlocking(false);  
  59.         SelectionKey selectionKey0 = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);  
  60.         selectionKey0.attach(new Acceptor());  
  61.     }  
  62.    
  63.    
  64.     public void run() {  
  65.         System.out.println("Server listening to port: " + serverSocketChannel.socket().getLocalPort());  
  66.         try {  
  67.             while (!Thread.interrupted()) {  
  68.                 int readySelectionKeyCount = selector.select();  
  69.                 if (readySelectionKeyCount == 0) {  
  70.                     continue;  
  71.                 }  
  72.                 Set<SelectionKey> selected = selector.selectedKeys();  
  73.                 Iterator<SelectionKey> it = selected.iterator();  
  74.                 while (it.hasNext()) {  
  75.                     dispatch((SelectionKey) (it.next()));  
  76.                 }  
  77.                   
  78.                 //不会自动remove,因此要手动清;下次事件到来会自动添加  
  79.                 selected.clear();  
  80.             }  
  81.         } catch (IOException ex) {  
  82.             ex.printStackTrace();  
  83.         }  
  84.     }  
  85.       
  86.     //从SelectionKey中取出Handler并执行Handler的run方法,没有创建新线程  
  87.     void dispatch(SelectionKey k) {  
  88.         Runnable r = (Runnable) (k.attachment());  
  89.         if (r != null) {  
  90.             r.run();  
  91.         }   
  92.     }  
  93.       
  94.     //主要工作是为每一个连接成功后返回的SocketChannel关联一个Handler,详见Handler的构造函数  
  95.     class Acceptor implements Runnable {  
  96.         public void run() {  
  97.             try {  
  98.                 SocketChannel socketChannel = serverSocketChannel.accept();  
  99.                 if (socketChannel != null) {  
  100.                     if (isWithThreadPool)  
  101.                         new HandlerWithThreadPool(selector, socketChannel);  
  102.                     else  
  103.                         new Handler(selector, socketChannel);  
  104.                 }  
  105.                 System.out.println("Connection Accepted by Reactor2");  
  106.             } catch (IOException ex) {  
  107.                 ex.printStackTrace();  
  108.             }  
  109.         }  
  110.     }  
  111.       
  112.     public static void main(String[] args) throws IOException{  
  113.           
  114.         int port = 9900;  
  115.         boolean withThreadPool = false;  
  116.         Reactor reactor  = new Reactor(port, withThreadPool);  
  117.         new Thread(reactor).start();  
  118.     }  
  119. }  

 

Java代码  收藏代码
  1. package com.ljn.reactor;  
  2.   
  3. import java.io.IOException;  
  4. import java.nio.ByteBuffer;  
  5. import java.nio.channels.SelectionKey;  
  6. import java.nio.channels.Selector;  
  7. import java.nio.channels.SocketChannel;  
  8.   
  9. /* 
  10.  * 单线程版本的Handler 
  11.  */  
  12. public class Handler implements Runnable {  
  13.    
  14.     final SocketChannel socketChannel;  
  15.     final SelectionKey selectionKey;  
  16.     ByteBuffer input = ByteBuffer.allocate(1024);  
  17.     static final int READING = 0, SENDING = 1;  
  18.       
  19.     //初始状态  
  20.     int state = READING;  
  21.     String clientName = "";  
  22.    
  23.     //在handler里面设置interestOps,而且这个interestOps是会随着事件的进行而改变的  
  24.     Handler(Selector selector, SocketChannel c) throws IOException {  
  25.         socketChannel = c;  
  26.         c.configureBlocking(false);  
  27.         selectionKey = socketChannel.register(selector, 0);  
  28.           
  29.         /* 
  30.         handler作为SellectionKey的attachment。这样,handler就与SelectionKey也就是interestOps对应起来了 
  31.         反过来说,当interestOps发生、SelectionKey被选中时,就能从SelectionKey中取得handler 
  32.         */  
  33.         selectionKey.attach(this);  
  34.         selectionKey.interestOps(SelectionKey.OP_READ);  
  35.         selector.wakeup();  
  36.     }  
  37.    
  38.     //在Reactor的dispatch方法里面被调用,但是直接的方法调用,没有创建新线程  
  39.     public void run() {  
  40.         try {  
  41.             if (state == READING) {  
  42.                 read();  
  43.             } else if (state == SENDING) {  
  44.                 send();  
  45.             }  
  46.         } catch (IOException ex) {  
  47.             ex.printStackTrace();  
  48.         }  
  49.     }  
  50.    
  51.     void read() throws IOException {  
  52.         int readCount = socketChannel.read(input);  
  53.         if (readCount > 0) {  
  54.             readProcess(readCount);  
  55.         }  
  56.         state = SENDING;  
  57.         // Interested in writing  
  58.         selectionKey.interestOps(SelectionKey.OP_WRITE);  
  59.     }  
  60.    
  61.     /** 
  62.      * Processing of the read message. This only prints the message to stdOut. 
  63.      * 非IO操作(业务逻辑,实际应用中可能会非常耗时):将Client发过来的信息(clientName)转成字符串形式 
  64.      * @param readCount 
  65.      */  
  66.     synchronized void readProcess(int readCount) {  
  67.         StringBuilder sb = new StringBuilder();  
  68.         input.flip();   //from writing mode to reading mode  
  69.         byte[] subStringBytes = new byte[readCount];  
  70.         byte[] array = input.array();  
  71.         System.arraycopy(array, 0, subStringBytes, 0, readCount);  
  72.         // Assuming ASCII (bad assumption but simplifies the example)  
  73.         sb.append(new String(subStringBytes));  
  74.         input.clear();  
  75.         clientName = sb.toString().trim();  
  76.     }  
  77.    
  78.     void send() throws IOException {  
  79.         System.out.println("Saying hello to " + clientName);  
  80.         ByteBuffer output = ByteBuffer.wrap(("Hello " + clientName + "\n").getBytes());  
  81.         socketChannel.write(output);  
  82.         selectionKey.interestOps(SelectionKey.OP_READ);  
  83.         state = READING;  
  84.     }  
  85. }  

 

Java代码  收藏代码
  1.     package com.ljn.reactor;  
  2.   
  3. import java.io.IOException;  
  4. import java.nio.channels.SelectionKey;  
  5. import java.nio.channels.Selector;  
  6. import java.nio.channels.SocketChannel;  
  7. import java.util.concurrent.ExecutorService;  
  8. import java.util.concurrent.Executors;  
  9.   
  10. /* 
  11.  * 多线程版本的Handler 
  12.  * 思路就是把耗时的操作(非IO操作)放到其他线程里面跑, 
  13.  * 使得Handler只专注与Channel之间的IO操作; 
  14.  * Handler快速地从Channel中读或写,可以使Channel及时地、更快地响应其他请求 
  15.  * 耗时的操作完成后,产生一个事件(改变state),再“通知”(由Handler轮询这个状态是否有改变) 
  16.  * Handler执行Channel的读写操作 
  17.  */  
  18. public class HandlerWithThreadPool extends Handler {  
  19.    
  20.     static ExecutorService pool = Executors.newFixedThreadPool(2);  
  21.     static final int PROCESSING = 2;  
  22.    
  23.     public HandlerWithThreadPool(Selector sel, SocketChannel c) throws IOException {  
  24.         super(sel, c);  
  25.     }  
  26.    
  27.     //Handler从SocketChannel中读到数据后,把“数据的处理”这个工作扔到线程池里面执行  
  28.     void read() throws IOException {  
  29.         int readCount = socketChannel.read(input);  
  30.         if (readCount > 0) {  
  31.             state = PROCESSING;  
  32.               
  33.             //execute是非阻塞的,所以要新增一个state(PROCESSING),表示数据在处理当中,Handler还不能执行send操作  
  34.             pool.execute(new Processer(readCount));   
  35.         }  
  36.         //We are interested in writing back to the client soon after read processing is done.  
  37.         //这时候虽然设置了OP_WRITE,但下一次本Handler被选中时不会执行send()方法,因为state=PROCESSING  
  38.         //或者可以把这个设置放到Processer里面,等process完成后再设为OP_WRITE  
  39.         selectionKey.interestOps(SelectionKey.OP_WRITE);  
  40.     }  
  41.    
  42.     //Start processing in a new Processer Thread and Hand off to the reactor thread.  
  43.     synchronized void processAndHandOff(int readCount) {  
  44.         readProcess(readCount);  
  45.         //Read processing done. Now the server is ready to send a message to the client.  
  46.         state = SENDING;  
  47.     }  
  48.    
  49.     class Processer implements Runnable {  
  50.         int readCount;  
  51.         Processer(int readCount) {  
  52.             this.readCount =  readCount;  
  53.         }  
  54.         public void run() {  
  55.             processAndHandOff(readCount);  
  56.         }  
  57.     }  
  58. }  

 

0 0
原创粉丝点击