author:板桥里人
出处:JDON.COM
Java的事件模式是动态响应系统重要的基础,在图形界面领域的事件模式已经有很多文章介绍,但是在服务器端我们会碰到更多的事件模式,这里本人试图总结一下:
事件直接驱动模式
事件模式的第一个要求就是性能的要求,需要直接而且快,Command模式是必须经常使用的,主要适合于迅速处理 前台的命令,Command模式往往是系统架构的重要部分,也是流程控制的主要模式。
Command模式经常Java的Reflect一起使用,因为系统的事件处理系统是处于动态变化的,随着功能要求扩展,就可能有动态变化事件处理响应系统,以Struts中action为例,我们知道,Structs的一个主要配置文件是struts-config.xml 如下:
<struts-config> <action-mappings> <action path="/login" type="com.javapro.struts.LoginAction"/> <action path="/logout" type="com.javapro.struts.LogoutAction"/> </action- mappings> </struts-config> |
它实际是个command和event的映射关系,通过这个配置文件,运行时动态装载相应的Action,完成Command模式, 我们检查LoginAction代码,就可以看出Command模式的基本特征:
public final class LoginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ................. } } |
很明显,典型的Command模式需要有一个接口.接口中有一个统一的方法,这里统一的方法就是execute;
比如我们有个实时系统,客户段向服务器发出不同编码代号,意味着不同的请求,不同的请求有不同的Handler进行 处理,Handler接口是:
public class Handler{ public byte[] handleRequest(); }
不同性质的处理过程继承这个Handler接口,如负责进入系统的处理过程:
public class EnterHandler implements Handler{ public byte[] handleRequest(){ //具体业务处理 ...... } }
调用Handler时是:
//从cache中获取这个requestId对应的Handler Handler handler = (Handler)cache.get(new Integer(reqId)); //调用handler的统一方法 handleRequest() byte[] outInf = handler.handleRequest();
以上是常用的一个事件驱动模式。它的特点是靠一个事件直接启动对应的事件处理器。
Chain of Responsibility职责链模式也应该属于这类,当事件到达后,让这个事件在我们提供的一批处理器中逐个挑选适合的处理器进行处理,这个模式缺点是显然的,性能丧失在逐个挑选 上,一般不推荐使用,这个模式适合在我们无法预知发生的事件内容时使用,因为不知道发生事件的具体情况,我们就无法在程序运行前事先为其指派相应的处理器,只能靠运行时,事件自己去摸索“撞运气”。
监控式事件模式
监控式事件模式就不同于事件直接驱动模式,它是借助第三者来监控和触发事件,这类事件的特点是: 有一个观察者置身事外在定期独立运行着,我们将我们要监听的事件向这个观察者注册,这样观察者就 代替我们来监听这个事件,应用客户端通过观察者来获得事件状况。
应用客户端有三种与观察者交互的方式:1.直接融合 2.推方式 3.拉方式。
直接融合就是说应用客户端自己就是观察者,两者融合,这样无疑应用客户端获得的触发时间是最快的。
推方式就是观察者一旦侦测到事件发生,立即将事件Push推到应用客户端;拉方式类似收取邮件,应用客户端在需要时才从观察者拉取事件。
JDK 1.4的None Blocking I/O是监控式事件模式的典型实现,Selector显然是一个监控I/O的第三者,当有外部事件进来,通过 调用Slector.select方法可以获得外部事件,从而进行处理,可参考我的本栏文章。
监控式事件模式适合使用在触发性质的场合,比如数据库后端触发器 界面触发 I/O触发 状态改变触发等。
我们以一个信件触发为例,这其实是个Observer应用例子:
比如用户提请服务器计算一个数据,如果用户同时要求将计算结果向自己信箱发一封,那么我们看如何设计?按照通常思维,这是一个次序问题,先在内存中计算数据,然后将结果发送到他的信箱,最后返回结果到用户端,我们知道信件的发送是耗时的,因此,有可能网络的原因造成信件发送很慢,这是用户就一直等不到他的计算结果,很显然,我们使用监控式事件模式来解决,让发信的事件由监控者去完成,只要需要时触发就可以了:
public class Computer extends Observable{ public Computer(){ //将sendMailObserver设定为本类的观察者。 addObserver(new sendMailObserver()); } ....... public void compute(String input,boolean needEmail,String email){ //计算操作 ......... if (needEmail){ //设置变化点 setChanged(); //如果需要发送 email,我们把email地址作为参数传送过去 notifyObservers(email); } } }
我们再来看监控观察者代码是如何写的?
public class sendMailObserver implements Observer{ public void update(Observable obj,Object email){ if (email instanceof String){ sendMail(email); } } }
这样服务器在执行compute方法时,就没有发送邮件的等待,一直接继续执行。
监控式事件模式和事件直接驱动模式可以在一个系统一起使用,外界信号通过事件直接驱动模式启动系统处理模块, 系统处理模块处理过程中,可以通过监控式事件模式来触发其它后台任务。这样一个架构非常适合实时处理系统。
既然事件处理模式是众多应用系统的基本模式,那么应该可以形成一个框架标准,JMX的notification Model就是这样一个架构设计。
JMX Notification Model
我们知道,JMX是提供了一种对 MBean资源执行控制和配置的管理机制,但这只是复杂的,分布式的系统中的一部分, 还有需要资源能够感应状态改变或者特定事件变化的机制,这就是JMX Notification Model。 在JMX Notification Model中均可以实现"事件直接驱动模式"和"监控式事件模式",这取决于你的应用需求。
JMX Notification Model允许MBean通过调用notifications广播事件,接受者只要注册为一个listerner, JMX的 MBean notification model 将会激活这个listerner注册一次,然后将一直接受到 来自广播者发出的各种事件。
事件模式有三个角色,第一个是事件发出者producer 然后是事件接受者Consumer,第三个 是要传输的事件。JMX notification model也是这样分别依靠下列组件来实现这三个角色:
A. NotificationBroadcaster接口, 事件广播发出者, 这个接口允许监听者在需要发出的notification中注册他们感兴趣的事件。
B. 通用事件(Notification),这是我们要传输的事件。 Notification事件能被直接使用,也能成为子类,这些都依赖于随同事件传输的信息。 通过使用通用事件类型,监听者将能接受来自广播者所有类型的事件。
C. NotificationListener接口, 事件监听者或者接受者, 用于接受来自广播者的任何notification信号。
D. NotificationFilter接口, 这个接口为notification的监听者提供一个对发出事件的过滤器。
E. NotificationEmitter 接口, 扩展了NotificationBroadcaster接口,当删除监听者时允许更多的控制功能。
只要是MBean,就既可以成为notification的发布广播者,也可以成为notification的监听者接受者,或者同时两者都可以。
Attribute Change Notifications
Attribute Change Notifications是一种特殊的notification, 任何时候MBean属性attribute 被修改,外界能够被通知到。
在JMX架构中,MBean能够在属性attribute变化发生时,发出通知,关于诊断属性变化的机制以及触发 通知事件并不属于JMX规定部分,每个MBean可以有自己独立的实现方式。
Timer Service
Timer Service触发器是在规定的日期和事件发出通知,它能够一一个恒定的间隙不断重复发出通知, 通知可以发往所有注册为接受timer通知的对象,Timer Service也是一个可管理的MBean,允许应用系统设置 一个可配置的调度程度。
Monitoring
通过使用monitoring service,一个或多个MBean属性将按规定间隔时间被监视,对于被观察的Mbean,监视器monitor将从它上面获得一个值,叫derived gauge,这个derived gauge可以是被观察属性的原值,也可以是一个数字性属性连续被观察值之差。
当derived gauge值满足一系列条件时,每个monitor server将会发出一个特定类型的通知。 这些条件都是在monitor被初始化时设定的,也可以通过monitor MBean的管理接口动态设定。
根据MBean内部属性值类型有三种monitor:
A.CounterMonitor - 使用Java的整数类型来观察属性,有一个行为特征:
a. 总是大于或等于零.
b. 能自增.
c. 能回滚.
B.GaugeMonitor - 使用java的整数或浮点类型观察属性。象gauge(测量仪器) 要么上升 要么下降减少。
C StringMonitor - 使用String类型观察属性.
事件处理架构
JMS是基于Socket的一种消息处理框架,原理类似于监控式事件模式,但是JMS已经把这种模式上升到架构的高度。不同JVM间也依靠JMS消息可以实现事件系统(注意是系统,不简单是一个小事件了)的触发和激活。
从上面JMS的架构图可以看出事件三个角色Producer和Consumer 以及事件信息本身Message.JMS就是在Producer和Consumer之间建立一个连接Connection.
JMS可实现同步或异步的事件触发机制,分别是通过Poin to Point(拉方式)和Pubilsh/Subscibe(推方式)具体完成,在分布式计算环境中,异步机制是非常重要,可以起到解耦作用,因为分布环境中单点错误或通讯问题是经常发生的,整个分布式系统不能总是依靠同步机制来可靠地传递事件或notification.
由此可见,事件处理模式从Java诸多架构到我们具体应用程序,随处可见,根据不同的应用需求选择不同的事件处理模式,才能真正挖掘Java的潜在性能。