文章目录
博文所在专栏里有更多相关内容,如IO模型概述、JAVA NIO等,欢迎阅读与交流。
文字来源于读书笔记及个人心得,可能有引用其他博文,若引用了你的文字请联系我,我会加上来源,或者删除相关内容。
三 Reactor反应器模式
使用了Reactor反应器模式的有:Nginx、Netty、Redis等
Reactor模式是基于Java NIO的,由两个组件组成——Reactor和Handler:
(1)Reactor反应器线程:负责响应IO事件,并将事件分发到相应的Handler。
(2)Handler处理器:将自身(handler)与IO事件绑定,非阻塞地负责事件的处理,完成真正的连接建立、通道读取、业务逻辑处理、结果写出到通道等。
大致流程:
- 创建反应器:
- 使用ServerSocketChannel监听新连接
- 创建选择器
- 将ServerSocketChannel注册到选择器,并对返回的选择键添加附件,附件为处理器对象
- 创建处理器:
- 获取当前通道,注册到选择器,监听你感兴趣的IO事件
- 将处理器自身作为附件添加到注册时返回的选择键
- 选择器进行通道读取、业务逻辑处理、通道写入等操作
单线程模式:
reactor反应器分发通道、handler读写通道都在一个线程中处理
//单线程Reactor反应器模式示例
//反应器线程
class Reactor implements Runnable {
final Selector selector;
final ServerSocketChannel serverSocket;
//Reactor初始化
Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false);
//第一步,接收accept事件,监听新连接
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
//SelectionKey的attach(Object o)方法可以将任何java对象作为附件添加到SelectionKey实例中,在单线程Reactor中用于添加处理器实例,此处添加了一个带有处理器实例的线程对象
sk.attach(new Runnable() {
public void run() {
try {
SocketChannel channel = serverSocket.accept();
if (channel != null)
new Handler(selector, channel);
} catch (IOException ex) { /* ... */ }
}
});
}
public void run() {
try {
while (!Thread.interrupted()) {
//获取就绪选择器
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
while (it.hasNext()) {
//分发收到的事件(Reactor主要任务)
SelectionKey k=(SelectionKey) (it.next());
//SelectionKey的attachment()可以获取attach(Object o)所添加的附件(java对象)
Runnable r = (Runnable) (k.attachment());
//调用之前注册的callback对象
if (r != null) {
r.run();
}
}
selected.clear();
}
} catch (IOException ex) { /* ... */ }
}
}
//处理器线程
class Handler implements Runnable {
final SocketChannel channel;
final SelectionKey sk;
ByteBuffer input = ByteBuffer.allocate(1024);
ByteBuffer output = ByteBuffer.allocate(1024);
static final int READING = 0, SENDING = 1;
int state = READING;
Handler(Selector selector, SocketChannel c) throws IOException {
channel = c;
c.configureBlocking(false);
//注册到选择器,监听读就绪事件
sk = channel.register(selector, SelectionKey.OP_READ);
//将处理器自身作为附件
sk.attach(this);
// //第二步,注册Read就绪事件
// sk.interestOps(SelectionKey.OP_READ);
//唤醒可能存在的select()带来的阻塞状态
selector.wakeup();
}
boolean inputIsComplete() {
/* ... */
return false;
}
boolean outputIsComplete() {
/* ... */
return false;
}
void process() {
/* ... */
return;
}
//业务逻辑处理
public void run() {
try {
if (state == READING) {
read();
} else if (state == SENDING) {
send();
}
} catch (IOException ex) { /* ... */ }
}
void read() throws IOException {
channel.read(input);
if (inputIsComplete()) {
process();
state = SENDING;
// Normally also do first write now
//第三步,接收write就绪事件
sk.interestOps(SelectionKey.OP_WRITE);
}
}
void send() throws IOException {
channel.write(output);
//write完就结束了, 关闭select key
//这里不能关闭,因为该选择器在反应器线程中还有用
if (outputIsComplete()) {
sk.cancel();
}
}
}
多线程模式
reactor反应器一个线程,handler处理器用线程池管理
示例与单线程类似,区别在于处理器中对通道数据的处理放在了线程池中进行:
class MthreadHandler implements Runnable {
/* ... */
synchronized void read() throws IOException {
// ...
channel.read(input);
if (inputIsComplete()) {
state = PROCESSING;
//使用线程pool异步执行
pool.execute(new Processer());
}
}
class Processer implements Runnable {
public void run() {
processAndHandOff();
}
}
synchronized void processAndHandOff() {
process();
state = SENDING;
// or rebind attachment
//process完,开始等待write就绪
selectionKey.interestOps(SelectionKey.OP_WRITE);
}
}
优点
1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;
2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;
3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;
4)可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;
缺点
1)相比传统的简单模型,Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试。
2)Reactor模式需要底层的Synchronous Event Demultiplexer支持,比如Java中的Selector支持,操作系统的select系统调用支持,如果要自己实现Synchronous Event Demultiplexer可能不会有那么高效。
3) Reactor模式在IO读写数据时还是在同一个线程中实现的,即使使用多个Reactor机制的情况下,那些共享一个Reactor的Channel如果出现一个长时间的数据读写,会影响这个Reactor中其他Channel的相应时间,比如在大文件传输时,IO操作就会影响其他Client的相应时间,因而对这种操作,使用传统的Thread-Per-Connection或许是一个更好的选择,或则此时使用改进版的Reactor模式如Proactor模式。