1. Reactor的由来
Reactor是一种广泛应用在服务器端开发的设计模式。Reactor中文大多译为“反应堆”,我当初接触这个概念的时候,就感觉很厉害,是不是它的原理就跟“核反应”差不多?后来才知道其实没有什么关系,从Reactor的兄弟“Proactor”(多译为前摄器)就能看得出来,这两个词的中文翻译其实都不是太好,不够形象。实际上,Reactor模式又有别名“Dispatcher”或者“Notifier”,我觉得这两个都更加能表明它的本质。
那么,Reactor模式究竟是个什么东西呢?这要从事件驱动的开发方式说起。我们知道,对于应用服务器,一个主要规律就是,CPU的处理速度是要远远快于IO速度的,如果CPU为了IO操作(例如从Socket读取一段数据)而阻塞显然是不划算的。好一点的方法是分为多进程或者线程去进行处理,但是这样会带来一些进程切换的开销,试想一个进程一个数据读500ms,期间进程切换到它3次,但是CPU却什么都不能干,就这么切换走了,是不是也不划算?
这时先驱们找到了事件驱动,或者叫回调的方式,来完成这件事情。这种方式就是,应用业务向一个中间人注册一个回调(event handler),当IO就绪后,这个中间人产生一个事件,并通知handler进行处理。这种回调的方式,也体现了“好莱坞原则”——“Don‘t call us,we‘ll call you”。
好了,我们现在来看Reactor模式。在前面事件驱动的例子里有个问题:我们如何知道IO就绪就个事件,谁来充当这个中间人?Reactor模式的答案是:由一个不断等待和循环的单独进程(线程)来做这件事,它接受所有handler的注册,并负责先操作系统查询IO是否就绪,在就绪后就调用指定handler进行处理,这个角色的名字就叫做Reactor。
2. Reactor与NIO
Java中的NIO可以很好的和Reactor模式结合。关于NIO中的Reactor模式,我想没有什么资料能比Doug Lea大神(不知道Doug Lea?看看JDK集合包和并发包的作者吧)在《Scalable IO in Java》解释的更简洁和全面了。NIO中Reactor的核心是Selector,我写了一个简单的Reactor示例,注意这里有一个核心的Reactor的循环(这种循环结构又叫做EventLoop):
package us.codecraft.netty.reactor; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; public class Handler implements Runnable { final SocketChannel socket = null; final SelectionKey sk; Handler(Selector sel) throws IOException{ socket.configureBlocking(false); sk = socket.register(sel,0); sk.attach(this); sk.interestOps(SelectionKey.OP_READ); sel.wakeup(); } @Override public void run(){ //TODO:处理逻辑 } }
package us.codecraft.netty.reactor; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.Iterator; import java.util.Set; public class Reactor implements Runnable{ Selector selector; public Reactor() throws IOException { selector = Selector.open(); } public void run(){ try{ while(!Thread.interrupted()){ //循环,等待事件 selector.select(); Set selected = selector.selectedKeys(); Interator it = selected.iterator(); while(it.hasNext()) //调用handler,处理事件 dispatch((SelectionKey)(it.next())); selected.clear(); } }catch(IOException ex){ } } void dispatch(SelectionKey k) { Runnable r = (Runnable) (k.attachment()); if(r != null) r.run(); } }
package us.codecraft.netty.reactor; import java.io.IOException; public class Main{ public static void main(String[] args) throws IOException { Reactor reactor = new Reactor(); new Handler(reactor.selector); reactor.run(); } }