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=============