Classic Service Designs
跳过传统的一个client连接就开启一个线程的模式
Basic Reactor Design
Reactor线程创建一个NIO的server,将Acceptor类放到SelectionKey的attach,interestOps(SelectionKey.OP_ACCEPT),selector.select();进行阻塞监听acceot,当client连接到server中,selector.select()放行,运行dispatch方法,dispatch方法中通过attachment获取存在Acceptor的类,会运行Acceptor类的run方法,Acceptor.run获取到SockerChannel以后实例化Handler,将SelectionKey的attach改成了Handler类,同时将interestOps(SelectionKey.OP_READ),监听SelectionKey(也就是client)的读事件。
当client发送数据时selector.select()放行,运行dispatch方法,dispatch方法中通过attachment获取存在Handler类(非连接时Acceptor),Handler.run方法判断读或者写就行读写操作。
package douglea;
import com.sun.xml.internal.stream.util.ThreadLocalBufferAllocator;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
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;
/**
* @author lzming
* @create 2019-01-12 12:15
*/
public class BaseicReactorDesign {
public static void main(String[] args) throws IOException {
Thread thread=new Thread(new Reactor(8080));
thread.start();//这里新建了一个线程
while (true);//堵塞住防止程序运行完
}
}
class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel serverSocket;
Reactor(int port) throws IOException { //Reactor初始化
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false); //非阻塞
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT); //分步处理,第一步,接收accept事件
sk.attach(new Acceptor()); //attach callback object, Acceptor《=这里传入的是Acceptor
}
public void run() {
try {
while (!Thread.interrupted()) {
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
while (it.hasNext())
dispatch((SelectionKey)(it.next())); //Reactor负责dispatch收到的事件
selected.clear();
}
} catch (IOException ex) { /* ... */ }
}
void dispatch(SelectionKey k) {
Runnable r = (Runnable)(k.attachment()); //调用之前注册的callback对象
if (r != null)
r.run();//这里只运行,没有start新建线程
}
class Acceptor implements Runnable { // inner
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null)
new Handler(selector, c);
}
catch(IOException ex) { /* ... */ }
}
}
}
final class Handler implements Runnable {
private static final int MAXIN = 100;
private static final int MAXOUT = 100;
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 sel, SocketChannel c) throws IOException {
socket = c; c.configureBlocking(false);
// Optionally try first read now
sk = socket.register(sel, 0);
sk.attach(this); //《=将Handler作为callback对象
sk.interestOps(SelectionKey.OP_READ); //第二步,接收Read事件
sel.wakeup();
}
boolean inputIsComplete() { /* ... */ return true;}
boolean outputIsComplete() { /* ... */ return true;}
void process() { /* ... */ }
public void run() {
try {
if (state == READING) read();
else if (state == SENDING) send();
} catch (IOException ex) { /* ... */ }
}
void read() throws IOException {
input.clear();
socket.read(input);
if (inputIsComplete()) {
process();
state = SENDING;
// Normally also do first write now
sk.interestOps(SelectionKey.OP_WRITE); //第三步,接收write事件
}
}
void send() throws IOException {
output=input;
output.flip();
socket.write(output);
if (outputIsComplete()) {
//sk.cancel(); //write完就结束了, 关闭select key
state = READING;
// Normally also do first write now
sk.interestOps(SelectionKey.OP_READ); //第三步,接收write事件
}
}
}
////上面 的实现用Handler来同时处理Read和Write事件, 所以里面出现状态判断
////我们可以用State-Object pattern来更优雅的实现
//class Handler { // ...
// public void run() { // initial state is reader
// socket.read(input);
// if (inputIsComplete()) {
// process();
// sk.attach(new Sender()); //状态迁移, Read后变成write, 用Sender作为新的callback对象
// sk.interest(SelectionKey.OP_WRITE);
// sk.selector().wakeup();
// }
// }
// class Sender implements Runnable {
// public void run(){ // ...
// socket.write(output);
// if (outputIsComplete()) sk.cancel();
// }
// }
//}