《Scalable IO in Java》 是java.util.concurrent包的作者,大师Doug Lea关于分析与构建可伸缩的高性能IO服务的一篇经典文章。原文地址:gee.cs.oswego.edu/dl/cpjslide…
主要讲了网络编程的演进过程,文中提到的Reactor模式被netty借鉴,书中的代码稍加补全即可运行,下面借助代理理解一下:
Classic Service Designs 传统的服务设计模式
Each handler may be started in its own thread. 每个handler都有单独的线程处理.
示例代码:
public class Server implements Runnable {
private static final int PORT = 8899;
private static final int MAX_INPUT = 4096;
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 (Exception ex) {
ex.printStackTrace();
}
}
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) {
ex.printStackTrace();
}
}
private byte[] process(byte[] cmd) {
String msg = new String(cmd);
System.out.println(msg);
return ("hello " + msg).getBytes();
}
}
}
在main方法中new Thread(new Server()).start(); 启动,通过 nc localhost 8899 即可访问。
构建高性能可伸缩的IO服务 在构建高性能可伸缩IO服务的过程中,我们希望达到以下的目标:
- 能够在海量负载连接情况下优雅降级;
- 能够随着硬件资源的增加,性能持续改进;
- 具备低延迟、高吞吐量、可调节的服务质量等特点;
而分发处理就是实现上述目标的一个最佳方式。
分发模式具有以下几个机制:
- 将一个完整处理过程分解为一个个细小的任务;
- 每个任务执行相关的动作且不产生阻塞;
- 在任务执行状态被触发时才会去执行,例如只在有数据时才会触发读操作;
在一般的服务开发当中,IO事件通常被当做任务执行状态的触发器使用,在hander处理过程中主要针对的也就是IO事件;
java.nio包就很好的实现了上述的机制:
- 非阻塞的读和写
- 通过感知IO事件分发任务的执行
所以结合一系列基于事件驱动模式的设计,给高性能IO服务的架构与设计带来丰富的可扩展性;
Reactor模式 Reactor也可以称作反应器模式,它有以下几个特点:
- Reactor模式中会通过分配适当的handler(处理程序)来响应IO事件,类似与AWT事件处理线程;
- 每个handler执行非阻塞的操作,类似于AWT ActionListeners 事件监听
- 通过将handler绑定到事件进行管理,类似与AWT addActionListener 添加事件监听;
首先我们明确下java.nio中相关的几个概念:
Channels
支持非阻塞读写的socket连接;
Buffers
用于被Channels读写的字节数组对象
Selectors
用于判断channle发生IO事件的选择器
SelectionKeys
负责IO事件的状态与绑定
基于Reactor模式的服务端设计代码示例:
class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel serverSocket;
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); //注册accept事件
sk.attach(new Acceptor()); //调用Acceptor()为回调方法
}
public void run() {
try {
while (!Thread.interrupted()) {//循环
selector.select();
Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> it = selected.iterator();
while (it.hasNext()) {
SelectionKey next = it.next();
dispatch(next); //dispatch分发事件
}
selected.clear();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
void dispatch(SelectionKey k) {
//不同的SelectionKey会attach不同的Runnable
//Reactor attach->Acceptor连接处理类
//Handler attach->Handler读写处理类
Runnable r = (Runnable)(k.attachment()); //调用SelectionKey绑定的调用对象
if (r != null) {
r.run();
}
}
// Acceptor 连接处理类
class Acceptor implements Runnable { // inner
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null) {
new Handler(selector, c);
}
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
}
public class Handler implements Runnable {
private static final int MAXIN = 4096;
private static final int MAXOUT = 4096;
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绑定到SelectionKey上
sk.interestOps(SelectionKey.OP_READ);
//唤醒Reactor的selector.select();
sel.wakeup();
}
boolean inputIsComplete() {
return true;
}
boolean outputIsComplete() {
return true;
}
void process() {
input.flip();
System.out.println("receive: " + new String(input.array()));
}
public void run() {
try {
if (state == READING) {
read();
} else if (state == SENDING) {
send();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
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 {
String msg = "hello xxx\n";
output.put(msg.getBytes());
output.flip();
socket.write(output);
if (outputIsComplete()) {
sk.cancel();
}
output.clear();
}
}
多线程模式
public class Handler2 implements Runnable {
private static final int MAXIN = 4096;
private static final int MAXOUT = 4096;
private static ExecutorService executorService = Executors.newFixedThreadPool(3);
final SocketChannel socket;
final SelectionKey sk;
ByteBuffer input = ByteBuffer.allocate(MAXIN);
ByteBuffer output = ByteBuffer.allocate(MAXOUT);
static final int READING = 0, SENDING = 1;
static final int PROCESSING = 3;
int state = READING;
Handler2(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绑定到SelectionKey上
sk.interestOps(SelectionKey.OP_READ);
//唤醒Reactor的selector.select();
sel.wakeup();
}
boolean inputIsComplete() {
return true;
}
boolean outputIsComplete() {
return true;
}
void process() {
input.flip();
System.out.println("receive: " + new String(input.array()));
}
public void run() {
try {
if (state == READING) {
read();
} else if (state == SENDING) {
send();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
synchronized void read() throws IOException {
socket.read(input);
if (inputIsComplete()) {
state = PROCESSING;
executorService.execute(new Processer());
}
}
void send() throws IOException {
String msg = "hello xxx\n";
output.put(msg.getBytes());
output.flip();
socket.write(output);
if (outputIsComplete()) {
sk.cancel();
}
output.clear();
}
class Processer implements Runnable {
public void run() {
processAndHandOff();
}
}
synchronized void processAndHandOff() {
process();
state = SENDING; // or rebind attachment
sk.interestOps(SelectionKey.OP_WRITE);
}
}