原文地址:http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf
大神之作原文,值得好好拜读。
笔记整理如下:
Classic Service Designs
Classic ServerSocket Loop
class Server implements Runnable {
public void run() {
try {
ServerSocket ss = new ServerSocket(PORT);
while (!Thread.interrupted())
new Thread(new Handler(ss.accept())).start();
// or, single-threaded, or a thread pool
} catch (IOException ex) { /* ... */ }
}
static class Handler implements Runnable {
final Socket socket;
Handler(Socket s) { socket = s; }
public void run() {
try {
byte[] input = new byte[MAX_INPUT];
socket.getInputStream().read(input);
byte[] output = process(input);
socket.getOutputStream().write(output);
} catch (IOException ex) { /* ... */ }
}
private byte[] process(byte[] cmd) { /* ... */ }
}
}
Reactor Pattern
Reactor responds to IO events by dispatching the appropriate handler
Handlers perform non-blocking actions
Basic Reactor Design
Single threaded version
java.nio Support
-
Channels
Connections to files, sockets etc that support non-blocking reads -
Buffers
Array-like objects that can be directly read or written by Channels -
Selectors
Tell which of a set of Channels have IO events -
SelectionKeys
Maintain IO event status and bindings
Example
class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel serverSocket;
// Reactor 1: Setup
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.attach(new Acceptor());
}
// Reactor 2: Dispatch Loop
public void run() { // normally in a new Thread
try {
while (!Thread.interrupted()) {
selector.select();
Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> it = selected.iterator();
while (it.hasNext()) {
dispatch(it.next());
}
selected.clear();
}
} catch (IOException e) { /* ... */ }
}
void dispatch(SelectionKey k) {
Runnable r = (Runnable) k.attachment();
if(r != null) {
r.run();
}
}
// Reactor 3: Acceptor
class Acceptor implements Runnable { // inner
@Override
public void run() {
try {
SocketChannel c = serverSocket.accept();
if(c != null) {
new Handler(selector, c);
}
} catch (IOException e) { /* ... */ }
}
}
}
final class Handler implements Runnable{
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;
// Reactor 4: Handler setup
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);
sk.interestOps(SelectionKey.OP_READ);
sel.wakeup();
}
boolean inputIsComplete() { /* ... */ }
boolean outputIsComplete() { /* ... */ }
void process() { /* ... */ }
// Reactor 5: Request handling
@Override
public void run() {
try {
if(state == READING) read();
else if(state == SENDING) send();
} catch (IOException e) { /* ... */ }
}
void read() throws IOException {
socket.read(input);
if(inputIsComplete()) {
process();
state = SENDING;
// Normally also do first write now
sk.interestOps(SelectionKey.OP_WRITE);
}
}
void send() throws IOException {
socket.write(output);
if(outputIsComplete()) {
sk.cancel();
}
}
}
Multithreaded Designs
Worker Thread Pools
Handler with Thread Pool
class Handler implements Runnable {
// uses util.concurrent thread pool
static PooledExecutor pool = new PooledExecutor(...);
static final int PROCESSING = 3;
// ...
synchronized void read() { // ...
socket.read(input);
if (inputIsComplete()) {
state = PROCESSING;
pool.execute(new Processer());
}
}
synchronized void processAndHandOff() {
process();
state = SENDING; // or rebind attachment
sk.interest(SelectionKey.OP_WRITE);
}
class Processer implements Runnable {
public void run() { processAndHandOff(); }
}
}
Multiple Reactor Threads
Selector[] selectors; // also create threads
int next = 0;
class Acceptor { // ...
public synchronized void run() { ...
Socket connection = serverSocket.accept();
if (connection != null)
new Handler(selectors[next], connection);
if (++next == selectors.length) next = 0;
}
}