Java NIO之Reactor模式

2014/10/30 20:17
阅读数 3.3K

Java NIO之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=============

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部