JavaNIO之Reactor模式
如下图所示,
Single Threaded Versioin指的是 Reactor 只有一个线程在处理 IO 事件,分发所有的IO事件,而具体的处理过程则是由Handler 去做。
那么一个Reactor系统只有一个Reactor,如果有100 个连接,那么就有100 个Handler 在处理。(看下面代码)
我就按我的理解说一下一次网络请求的过程:
1.如下面Reactor的构造方法,启动一个Reactor系统。
public Reactor(int port) throws IOException { selector = Selector.open(); serverSocket = ServerSocketChannel.open(); serverSocket.socket().bind( new InetSocketAddress(port)); serverSocket.configureBlocking(false); SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT); //利用sk的attache功能绑定Acceptor 如果有事情,触发Acceptor sk.attach(new Acceptor()); log.info("->attach(new Acceptor())"); }
启动的时候把当前的 serverSocket 注册到给定的selector,并且指明感兴趣的事件,SelectionKey.OP_ACCEPT,然后返回一个SelectionKey,这个key表示当前的channel 和 selector的映射关系。
2.如果现在有一个网络连接,如果网络的OP_ACCEPT事件发生,则调用selector.selectedKeys();会得到一个关于OP_ACCEPT事件的key,然后dispatch(sk);分发这个事件。通过key的attachment()方法得到附加的对象,这个对象是一个线程对象,也是Acceptor对象。在这里处理网络连接,得到客户端的socketchannel。
3.得到了客户端的socketchannel,就可以准备读写客户端的socketchannel了。先注册一个SelectionKey.OP_READ读事件。并且当前的Handler对象附加到key对象上sk.attach(this);。
MulitiHandler(Selector selector, SocketChannel c) throws IOException { socket = c; c.configureBlocking(false); // Optionally try first read now sk = socket.register(selector, 0); // 注意在Handler里面又执行了一次attach,这样,覆盖前面的Acceptor, // 下次该Handler又有READ事件发生时, // 将直接触发Handler.从而开始了数据的读->处理->写->发出等流程处理。 sk.attach(this); sk.interestOps(SelectionKey.OP_READ); selector.wakeup(); }
4.当READ事件发生后,则会通过dispatch(sk);分发。通过Handler的run方法进行具体的IO的读操作。
5.读完了数据之后,注册OP_WRITE事件sk.interestOps(SelectionKey.OP_WRITE)。然后当该事件发生后,则分发该事件,调用Handler的run事件处理IO写操作。
如下代码示例,
package com.usoft; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class Reactor implements Runnable { private static Logger log = LoggerFactory.getLogger(Reactor.class); final Selector selector; final ServerSocketChannel serverSocket; public Reactor(int port) throws IOException { selector = Selector.open(); serverSocket = ServerSocketChannel.open(); serverSocket.socket().bind( new InetSocketAddress(port)); serverSocket.configureBlocking(false); SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT); //利用sk的attache功能绑定Acceptor 如果有事情,触发Acceptor sk.attach(new Acceptor()); log.info("->attach(new Acceptor())"); } // Alternatively,use explicit SPI provider : // SelectorProvider p = SelectorProvider.provider(); // selector=p.openSelector(); // serverSocket=p.openServerSocketChannel(); // class Reactor continued public void run() { // normally in a new Thread try { while (!Thread.interrupted()) { selector.select(); Set selected = selector.selectedKeys(); Iterator it = selected.iterator(); //Selector如果发现channel有OP_ACCEPT或READ事件发生,下列遍历就会进行。 while (it.hasNext()) { //来一个事件 第一次触发一个accepter线程 //以后触发Handler SelectionKey sk = (SelectionKey) it.next(); log.info(">>>>>>acceptable=" + sk.isAcceptable() + ",readable=" + sk.isReadable() + ",writable=" + sk.isWritable()); dispatch(sk); } selected.clear(); } } catch (IOException ex) { log.info("reactor stop!" + ex); } } void dispatch(SelectionKey k) { Runnable r = (Runnable) (k.attachment()); if (r != null) { r.run(); } } // class Reactor continued class Acceptor implements Runnable { // inner public void run() { try { log.debug("-->ready for accept!"); SocketChannel c = serverSocket.accept(); if (c != null) new Handler(selector, c); } catch (IOException ex) { /* . . . */ } } } }
package com.usoft; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; final class Handler implements Runnable { private static Logger log = LoggerFactory.getLogger(Reactor.class); static final int MAXIN = 1024; static final int MAXOUT = 1024; final SocketChannel socket; final SelectionKey sk; ByteBuffer input = ByteBuffer.allocate(MAXIN); ByteBuffer output = ByteBuffer.allocate(MAXOUT); static final int READING = 0, SENDING = 1; int state = READING; Handler(Selector selector, SocketChannel c) throws IOException { socket = c; c.configureBlocking(false); // Optionally try first read now sk = socket.register(selector, 0); // 注意在Handler里面又执行了一次attach,这样,覆盖前面的Acceptor, // 下次该Handler又有READ事件发生时, // 将直接触发Handler.从而开始了数据的读->处理->写->发出等流程处理。 sk.attach(this); sk.interestOps(SelectionKey.OP_READ); selector.wakeup(); } boolean inputIsComplete() { return true; //只是返回true,具体的判断没有实现 } boolean outputIsComplete() { return true;//只是返回true,具体的判断没有实现 } void process() { //没有具体实现 output.put("helloworld".getBytes()); } // class Handler continued public void run() { try { if (state == READING) read(); else if (state == SENDING) send(); } catch (IOException ex) { /* . . . */ } } void read() throws IOException { log.info("->read into bytebuffer from socketchannel inputs"); socket.read(input); if (inputIsComplete()) { log.info("->read complete"); process(); state = SENDING; // Normally also do first write now // 读完了数据之后,注册OP_WRITE事件 sk.interestOps(SelectionKey.OP_WRITE); } } void send() throws IOException { log.info("->write into socketchannel from bytebuffer outputs"); socket.write(output); if (outputIsComplete()) { /** * The key will be removed fromall of the selector's key sets * during the next selection operation. */ sk.cancel(); socket.close(); //关闭通过,也就关闭了连接 log.info("->close socketchannel after write complete"); } } }
ReactorTest.java
package com.usoft; import java.io.IOException; /** * Created by liyanxin on 2015/3/23. */ public class ReactorTest { public static void main(String args[]) throws IOException { Reactor reactor = new Reactor(9098); reactor.run(); } }
参考:http://www.jdon.com/concurrent/reactor.htm
===========================END===========================