Reactor Pattern 是一种为处理服务请求并发提交到一个或者多个服务处理程序的事件设计模式,当请求抵达后,服务处理程序使用多路分配策略,然后同步地派发这些请求至相关的请求处理程序。
结构
从结构上说, Reactor 设计模式具有 4 个要素。
资源 ,系统可以提供输出或者被输入
同步事件多路分配器 ,适用事件循环方式阻止所有的资源,当可以在一个无锁资源上启动一个同步操作时,同步事件多路分配器发送这个资源到适配器。
适配器 ,处理请求处理程序注册和注销,从同步事件多路分配器派发资源给相关的请求处理程序。
请求处理程序 ,应用定义的实际处理请求的程序和相关的资源。
优势
从定义上讲,所有的 reactor 系统是单线程模式,但是它也可以应用到多线程环境之中。
这种设计模式完全分割了应用业务代码和 reactor 模式的实现代码,这意味着业务组件可以模块化划分,可重用。同时,因为同步调用请求处理程序,当没有向系统中增加复杂多线程时, reactor 设计模式允许简化粗粒度并发。
限制
相对于别的过程化设计模式,因为逆向控制, Reactor 模式非常难与调试。除此外,只通过同步的方式调用请求处理程序, reactor 模式限制了最大并发量,特别是在 SMP 硬件上。 Reactor 的可伸缩性不但受到同步调用请求处理程序的限制,还受到多路分配器的限制。早些版本的 Unix 的 select 和 poll 调用都有一个最大的描述符,当这个描述符设置过大的时候会带来性能的问题。最近,更具有伸缩性的这类系统已经被设计出来,如 Solaris 的 /dev/poll, Linux 的 epoll, 基于 BSD 系统的 kqueue/kevent ,这些系统实现了高描述符情况下的高性能。
Reactor 模式与 Java 网络多线程
Reactor模式的设计目的是为了解决服务器环境下多线程的问题,直JDK1.4后,Java积极推广NIO和Selector模式的Server和Comsumer网络编程模式,但是Selector模式的编程没有传统的Stream模式显得优雅,这里,先用传统的Stream网络IO编程方式来实现一个reactor模式。
依据前面的描述,照猫画虎,设计Dispatcher,Demishplexer, RequestHander分别对应派发器,多路分发器和请求处理程序,至于资源,在这个例子中就抽象成一个数组,数组中的内容代表处理request时需要用到的资源,在实际的应用中,资源可能是一个数据连接,RPC连接等。在这个模式中,资源一旦被初始化,任何代码不可以改变数组中的内容,也即不改变resource。多路分发器根据当前数组中的元素决定是否分发“资源”给Dispatcher。另外还有一个requester来模拟客户端请求任务。
为了简单起见,本例中直接在分发器中初始化资源。分发器有两个重要的方法,accept方法接受request并且判断是否有资源给dispatcher,returnResource方法是在任务处理程序执行完后归还资源给分发器。
class DemultiPlexer { // initialize resources private ArrayBlockingQueueresources = new ArrayBlockingQueue (2); { resources.add(1); resources.add(2); } private ArrayList requests = new ArrayList (5); /** * * @param s * the starting time of get resource action * @param timeout * , limit the request resource action within a time period in * second * @return a integer represent a resource requesting */ public synchronized Integer getResource(long s, int timeout) { for (;;) { if ((System.currentTimeMillis() - s) / 1000 > timeout) { throw new RuntimeException("time out to get resource"); } else if (resources.size() > 0) { return resources.poll(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } public void returnResource(Integer i) { synchronized (resources) { resources.add(i); } } /** * accept a request and create a new dispatcher to assign the resource to a * request handler. * * @param Request */ public void accept(Integer requestId) { requests.add(requestId); Integer rid = getResource(System.currentTimeMillis(), 5); Dispatcher d = new Dispatcher(this, requestId, rid); d.createRequestHandler().start(); } }
Dispatcher拿到资源后,负责创建新的任务处理程序,这里是直接创建,在实际应用中,任务处理程序可能会被放到线程池中去执行。
class Dispatcher { private Integer requestId; private Integer resourceId; private DemultiPlexer demultiPlexer; public Dispatcher(DemultiPlexer demultiPlexer, Integer requestId, Integer resourceId) { super(); this.requestId = requestId; this.resourceId = resourceId; this.demultiPlexer = demultiPlexer; } /** * create a request handler when demultiPlexer has an ideal resource. * * @return RequestHandler */ public RequestHandler createRequestHandler() { return new RequestHandler(this, requestId, resourceId); } /** * when request handler complete task, free the resource assigned. */ public synchronized void freeResource(Integer i) { demultiPlexer.returnResource(i); } // getters & setters ...... }
RequestHandler才是真正的任务执行者。执行完后,需要归还资源,以便其他的任务执行者能请求道资源。
class RequestHandler extends Thread { private Integer tid; private Integer rid; private Dispatcher dispatcher; public RequestHandler(Dispatcher dispatcher, Integer tid, Integer rid) { this.tid = tid; this.rid = rid; this.dispatcher = dispatcher; } @Override public void run() { System.out.println("the request No.[" + tid + "] is handling the requesting with resource [" + rid + "]"); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("the request No.[" + tid + "] processed the requesting with resource [" + rid + "]"); // free resource after request was handled dispatcher.freeResource(rid); System.out.println("return resource: " + rid); } }
Requester类是请求程序,该类的run方法在连上socket后发生一个自身的标识符requestid, 在main函数中启动10个requester线程,来模拟多用户并发访问的情形。
class Requester extends Thread { private String address = "localhost"; private int port = 1220; private int requestID; @Override public void run() { try { Socket socket = new Socket(address, port); socket.getOutputStream().write(requestID); sleep(5000); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("No." + requestID + " was sent..."); } public static void main(String args[]) { for (int i=0; i< 10 ; i++){ new Requester(i).start(); } } }
有了这几个类后,再启动server端来处理这些请求就可以构成一个完整的reactor模式的代码。这个类启动serversocket读取requester发送过来的requestid,交给分发器去处理,serversocket闲置30s后自动关闭,另外这个类还注册了一个shutdownhook,在程序异常终止时,确保serversocket被关闭。
public class ReactorPattern { DemultiPlexer demultiPlexer = new DemultiPlexer(); public static void main(String args[]) throws InterruptedException, IOException { ReactorPattern reactorPattern = new ReactorPattern(); ServerSocket server = new ServerSocket(1220); Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(server)); long s = System.currentTimeMillis(); for (;;) { if (System.currentTimeMillis() - s > 30000) break; Socket socket = server.accept(); int i = socket.getInputStream().read(); if (i != -1) { reactorPattern.getDemultiPlexer().accept(i); } } server.close(); } public DemultiPlexer getDemultiPlexer() { return demultiPlexer; } }
辅助类ShutdownHookThread,
class ShutdownHookThread extends Thread { private ServerSocket server; public ShutdownHookThread(ServerSocket server) { this.server = server; } @Override public void run() { try { server.close(); } catch (IOException e) { e.printStackTrace(); } finally { server = null; } } }
先执行ReactorPattern类,再执行Requester类。输出如下:
the request No.[7] is handling the requesting with resource [1] the request No.[8] is handling the requesting with resource [2] the request No.[7] processed the requesting with resource [1] return resource: 1 the request No.[8] processed the requesting with resource [2] return resource: 2 the request No.[5] is handling the requesting with resource [1] the request No.[1] is handling the requesting with resource [2] the request No.[5] processed the requesting with resource [1] the request No.[1] processed the requesting with resource [2] return resource: 1 return resource: 2 the request No.[9] is handling the requesting with resource [1] the request No.[4] is handling the requesting with resource [2] the request No.[4] processed the requesting with resource [2] the request No.[9] processed the requesting with resource [1] return resource: 1 return resource: 2 the request No.[0] is handling the requesting with resource [1] the request No.[3] is handling the requesting with resource [2] the request No.[0] processed the requesting with resource [1] the request No.[3] processed the requesting with resource [2] return resource: 1 return resource: 2 the request No.[2] is handling the requesting with resource [1] the request No.[6] is handling the requesting with resource [2] the request No.[2] processed the requesting with resource [1] return resource: 1 the request No.[6] processed the requesting with resource [2] return resource: 2
小结
Reactor模式在分发资源和归还资源的时候是同步的,在处理请求的时候,是异步的,实际使用中,真正的业务代码会被放到请求处理程序之中。
附件中有完整的代码共大家学习研究。
[原创内容,版权所有,如果转载,请注明出处]