在web开发中, 我们最常用的模式就是MVC,将web程序的不同部分分而治之。在Nio网络程序开发中,也有一些类似的开发模式,比如Reactor模式,来方便我们对程序进行解耦和控制。
维基百科对Reactor pattern
的解释:
The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers
从定义可以看出,Reactor模式主要有以下特点:
- 事件驱动
- 请求分发
- 事件处理
我们通过NIO的编程例子去理解这三个部分:
1、事件驱动:
在编写程序的过程中,最习惯的方式是:在哪种条件下,将执行哪段代码
if(condition1) {
//event1
}
else if(condition2) {
//event2
}
//...
现在,我们用多态的方式实现这个逻辑
pulic class Condition1 implements Runable {
run() {
//event1
}
}
pulic class Condition2 implements Runable {
run() {
//event2
}
}
Runable r = new Runable() //Condition1、Condition2 这两个其中一个
r.run()
...
通过这种方式,能够根据r实际的类型去执行对应的逻辑,对于Reator模式,也是如此,拿Nio网络编程来举例,我们会提前在每一个SelectionKey上注册对应的响应事件程序,等到事件发生时,再进行回调:
public class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel ssc;
public Reactor(int port) throws IOException {
selector = Selector.open();
ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(port));
ssc.configureBlocking(false);
//注册事件处理程序Acceptor
ssc.register(selector, SelectionKey.OP_ACCEPT, new Acceptor(selector, ssc));
}
public void run() {
while (!Thread.interrupted()) {
try {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
dispatch(keyIterator.next());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void dispatch(SelectionKey key) {
//事件发生时 回调事件响应程序
Runnable r = (Runnable) key.attachment();
if(r != null) {
r.run();
}
}
}
public class Handler implements Runnable {
final SelectionKey sk;
final SocketChannel sc;
static final int MAXIN = 10000, MAXOUT = 10000;
ByteBuffer input = ByteBuffer.allocate(MAXIN);
ByteBuffer output = ByteBuffer.allocate(MAXOUT);
static final int READING = 0, SENDING = 1, PROCESSING = 3;
int state = READING;
static ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10,
0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
public Handler(Selector selector, SocketChannel sc) throws IOException {
this.sc = sc;
sc.configureBlocking(false);
//注册事件处理程序
sk = sc.register(selector, SelectionKey.OP_READ, this);
selector.wakeup();
}
public void run() {
try {
if (state == READING) {
read();
} else if (state == SENDING) {
send();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//...
}
2、事件分发:
事件分发对应于多路复用的网路事件,即接收就绪、读就绪、写就绪和连接就绪。对于不同的事件,交由不同的逻辑处理。在例子中事件分发通过Java的多态隐式的进行了分发。
3、事件处理:
上面的过程干了两件事,响应请求和事件转发,第三件事情就是真正的处理请求,也就是事件处理Handler
public class Handler implements Runnable {
final SelectionKey sk;
final SocketChannel sc;
static final int MAXIN = 10000, MAXOUT = 10000;
ByteBuffer input = ByteBuffer.allocate(MAXIN);
ByteBuffer output = ByteBuffer.allocate(MAXOUT);
static final int READING = 0, SENDING = 1, PROCESSING = 3;
int state = READING;
static ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10,
0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
public Handler(Selector selector, SocketChannel sc) throws IOException {
this.sc = sc;
sc.configureBlocking(false);
sk = sc.register(selector, SelectionKey.OP_READ, this);
selector.wakeup();
}
public void run() {
try {
if (state == READING) {
read();
} else if (state == SENDING) {
send();
}
} catch (IOException e) {
e.printStackTrace();
}
}
synchronized void read() throws IOException {
sc.read(input);
if (inputIsComplete()) {
state = PROCESSING;
pool.execute(new Processor());
}
}
synchronized void send() throws IOException {
sc.write(output);
if (outputIsComplete()) {
sk.cancel();
}
}
private boolean inputIsComplete() {
return true;
}
private boolean outputIsComplete() {
return true;
}
private void process() { /* ... */ }
synchronized void processAndHandOff() {
process();
state = SENDING; // or rebind attachment
sk.interestOps(SelectionKey.OP_WRITE);
}
public class Processor implements Runnable{
@Override
public void run() {
processAndHandOff();
}
}
}
总结:对于Reactor的理解主要是将网络请求分为三个部分:响应请求,转发请求和处理请求。响应请求是基于注册响应程序的事件驱动的方式。在学习Reactor的过程中写了一个简单的demo:GitHub连接。本人对网络编程的认识也是刚刚开始,理解的不对的地方欢迎讨论,在此提前谢过。