UML软件工程组织

基于JMX通知框架的AJAX深度开发
作者:朱先忠编译 出处:天极开发

  文章提示 AJAX和JMX分别位于系统管理栈的两个对立端。然而,如今AJAX模型已普遍存在于丰富的浏览器客户端。这使得该模型在构架领域为在问题解析管道中提高支持模式所具有的优点变得模糊起来。

 本文将详细描述一种AJAX架构的优点-它可以把管理状态"广播"到一个能够使用浏览器的用户基上而不必等待页面更新。

 这种架构是一个通用模型的扩展-可用于把JMX事件和属性记载到一个服务器端日志文件中;而且,这种变化进一步把管理信息记录或"广播"到(支持AJAX的)用户基。

 在本文中,我们将特别分析AJAX请求/响应模型和把管理数据绘制到页面的过程,还有漂亮的JMX通知框架-所有这些都通过一个装配的servlet清晰地集成到一起。

 接下来我们还会粗略地看一下通常在标准AJAX讨论中所不及的内容-安全和容量模型。

 本文中,我们使用BEA WebLogic 8.1用作这些软件的发布平台,尽管这里的架构和方法也适用于其它J2EE应用程序服务器。

 一、关键需求

 针对于企业Java和J2EE应用程序的系统管理栈形成了问题解析管道的一部分-在此,Java/J2EE应用程序与一个管理层进行交互以监视潜在的问题,例如应用程序服务器线程饥饿,堆溢出或到一个数据库的陈旧连接等。

 管理层通常包括JMX Mbeans-要装配并使用哪个应用程序,以及其它一些产品例如读取这些JMX属性的Wily Introscope和HP OpenView-如果超过一个事先配置的门槛值,那么它能够从Wily中得到警告。

 这个模型存在一个问题-如果系统在后台出现问题的话,针对JMX管理的服务器端模式将无法帮助浏览器端客户,因为这种模型全部是以服务器为中心的。例如,如果一新的J2EE Web应用程序要被发布或该应用程序在几分钟内将要关闭-因为Wily检测到一个问题,那么在浏览器上的用户并不知道发生了这么紧迫的管理事件!

 通过全面地允许用户进入问题解析管道,系统管理员可以管理终端用户体验-通过把管理信息广播到用户基并在一定程度上控制用户的行为来实现。

 为了通过HTTP协议实现把管理信息传递给客户,存在这样一个问题:如果客户端用户不是公开地使用GET或POST更新页面而是偷偷地更新一隐蔽的框架,那么管理方面如何把管理信息发送到一个HTTP客户?

 二、方案描述

 本文要实现一简单的AJAX脚本,它将从MBean服务器取得以XML消息形式的管理信息并经由一个servlet解决这个问题。这些管理信息必须被加以管理并且被反馈到所有的参与其中的能够接收AJAX请求的应用程序服务器。

 在服务器端:

 ·一个J2EE应用程序服务器簇用于服务于来自基于浏览器的用户和在线事务处理(OLTP)用户基(例如有两个或四个服务器)的请求。通过使用一个第三方Web服务器,用户请求被跨越该簇(OLTP簇)平衡装载。

 ·一个标准MBean(UserWeb)用于存储管理信息,如加上元数据属性的管理消息。该Mbean宿主在J2EE"管理"服务器和在OLTP簇上的J2EE服务器上。

 ·在管理服务器上,由系统管理员使用针对JMX的HTMLAdaptor来设置警告状态,重试间隔(在相邻XMLHttp-Requests之间的间隔)以及警告消息-例如"System Down in 10 Minutes."。然后,管理员把该状态广播到管理服务器上的Mbeans-由该管理服务器来把它们的状态重置到master管理状态。

 在客户端:

 ·支持AJAX的客户端使用一个XMLHttpRequest(它调用一个servlet以返回XML消息形式的相关的MBean值)来检索状态、重试间隔和消息。

 ·然后由客户端JavaScript分析这个XML消息,重置重试间隔并且用管理消息重画屏幕的一部分。

 ·在retry-interval秒之后,该客户进行另外一个XMLHttpRequest并且客户周期再次开始。

 三、 基本架构

 图1显示出全面的解决方案体系结构,其中表1描述了相应的基本架构元素。

  

  

  一) JMX通知模型

 这个模型包含两个部件:

 ·MBean-为本地和远程注册的听者激活事件

 ·听者-它用MBean注册自己以听取由该MBean所产生的事件

 第一个由UserWeb Mbean来实现,第二个由ManagementListener来实现。

 (二) 管理用户信息的JMX MBean

 UserWeb标准的MBean是一个简单类-它包含关键的属性和方法(表2)。



  (三) 事件听者

 Singleton ManagementListener类实现了Weblogic.management.RemoteNotificationListener-它扩展了javax.management.NotificationListener和java.rmi.Remote以允许在一个远程WebLogic JVM上的事件通过使用RMI技术被通知到远程听者。
在应用程序服务器启动时,在每个JVM上的一个听者用管理服务器上的UserWeb MBean注册自己。

 (四) MBean助理

 使用一个助理类来对Mbeans加以包装是个不错的注意。这样,我们可以从装配的代码中调用这个助理从而调用MBean方法。
UserWebMBeanHelper类被用作UserWeb Mbean的包装。所有助理的祖先是ApplicationMBeanHelper,它负责:

 ·查找本地和远程MBean服务器

 ·调用这些服务器以取得/设置MBean属性并且调用MBean方法

 为了确保相匹配,MBean和MBean助理都实现接口UserWebMBean。

 (五) 装配Servlet

 一个应用程序可以被装配以使用JMX。用AOP术语来说就是,把管理方面织入到应用程序代码中。本文中第一个JMX装配点是一个HTTPServlet。这个servlet是AJAX请求的目标,并且它实现一个控制器模式-它可以被精心制作以使用简单的请求参数来处理其它AJAX请求。
从一个MVC的角度来看,该模型是UserWeb Mbean,视图是支持AJAX的(JSP)页面,而控制器是被装配的servlet。

 (六) 客户端AJAX引擎

 这是一组JavaScript函数,它们:

 ·管理XMLHttpRequest并且响应处理重复性操作

 ·分析由XMLHttpRequest返回的XML消息

 ·用XML消息内容重画屏幕

 客户描述

 这是main.jsp页面-它包含客户端AJAX引擎和可重画的部分。

 (七) 序列

 实质上,服务器端序列参与管理管理属性的设置并且把这些属性广播到所有的感兴趣(听)的JVM上。而,客户端序列参与检索这些属性并且以管理指定的间隔时间用重要的管理信息来重画该HTML页面。

 (八) JMX通知(服务器序列)

 ·UserWeb MBeans和MBean事件听者在应用程序服务器启动时被使用相应的启动类创建并且注册

 ·管理员设置"master"UserWeb MBean属性(警告消息和重试间隔),然后向宿主在远程管理服务器上的听者广播或通知这一状态

 ·远程听者处理通知-通过把master(通知)数据复制到本地UserWeb MBean实现

 (九) XMLHttpRequest查询(客户序列)

 ·支持AJAX的客户端间隔地调用一个servlet以查询管理状态

 ·该servlet读取本地UserWeb MBean属性,然后把它们插入到一个XML消息中并且返回该XML消息作为一个到浏览器客户的XML响应(以后讨论可供选择的消息格式)

 ·然后,AJAX客户分析XML文档,提取警告和重试间隔等消息,重画屏幕,然后使用这一重试间隔来设置下一个XMLHttpRequest的延迟时间。

 下面详细描述其中的每一步。

  四、注册MBeans和MBean听者

 在每一个J2EE服务器实例上,在服务器启动时运行两个启动类:

 ·ManagementStartup-它把UserWeb MBean注册到本地MBean服务器。Startup类参数包括警告状态的默认设置,还有MBean名称和MBean类。例如:

<StartupClass
Arguments="ServerName=admin,
MBeanName=ExampleApp:Name=UserWeb,
MBeanClass=com.grahamh.management.userWeb.UserWeb"
ClassName="com.grahamh.management.startup.ManagementStartup"
FailureIsFatal="true" Name="UserWEB" Notes=""
Targets="admin,OLTPCluster"/>

 ·MbeanRegistrations-它用管理服务器上的UserWeb MBean来注册一个Singleton POJO-ManagementListener。

 一个javax.management.NotificationFilterSupport对象被用于列举UserWeb MBean将生成和听者将接收的通知的类型:

//MbeanRegistrations.java
MBeanHelperFactory.getWebHelper().registerListener();
//UserWebMbeanListener.java
public void registerListener() throws UserWebException{
try {
 //得到听者和过滤
 ManagementListener listener = MBeanHelperFactory.getListener();
 NotificationFilterSupport filter = listener.getSupportedEvents();
 //得到admin mbean服务器;
 //用UserWeb MBean注册该听者和过滤
 RemoteMBeanServer rmbs = getAdminMbeanServer();
 rmbs.addNotificationListener("ExampleApp:Name=UserWeb", listener, filter, null);
}
catch (Exception e) {
 throw new UserWebException("Unable to registerListener: "+ e.getMessage(), e);
}
}

 该listener.getSupportedEvents()方法返回下面的过滤器(filter):

NotificationFilterSupport filter = new NotificationFilterSupport();
filter.enableType("alert.broadcast");

 当ManagementListener在服务器启动时,在(远程的)管理服务器上建立一个到MBean服务器的连接,而且(本地的)ManagementListener被注册为一个听者-听取在UserWeb MBean上生成的事件,并且有一个过滤器被设置为"alert.broadcast"事件类型。

 因为该ManagementListener实现Weblogic.management.RemoteNotificationListener,所以它可以得到在本地JVM或一远程JVM上生成的JMX通知;在本文中,是指在远程管理服务器JVM上生成的JMX通知。

 五、广播Admin MBean属性

 管理和托管UserWeb Mbeans可以进行独立地设置-这给任何一个J2EE服务器一个本地化的AJAX响应。然而,一个普通操作,管理和支持(OA&M)支持模式将设置admin MBean的属性,然后使用通知模型把这些属性广播到远程应用程序服务器上的MBeans,以备随后的AJAX检索之用。

 因为该UserWeb MBean是基于ApplicationMBean-它扩展了javax.management.NotificationBroadcasterSupport,所以该基础结构正适合于由UserWeb MBean来通知所有的听者。因此,管理员设置相关的MBean属性(使用HTMLAdaptor)并且点击BroadcastState(见图2)。

 图2 使用HTMLAdaptor看到的MBean视图

 因而,UserWeb.broadcastState()方法被执行-它同步地通知所有的听者有关admin MBean的状态:

public void broadcastState() throws Exception {
 try {
  Notification n = new Notification("alert.broadcast", "ExampleApp:Name=UserWeb", 0);
  n.setUserData(new UserWeb(this));
  this.sendNotification(n);
 }
 catch (Exception e) {
  throw e;
 }
}

 因为数据在网络上是串行化传输的,所以这种并非暂时的对象图必须是可串行化的。

 六、使用听者接收来自MBean Props的通知

 所谓的事件听者就是ManagementListener Singleton。在管理服务器上的JMX通知框架远程调用ManagementListener中的handleNotification()方法-该方法存在于每个OLTP簇JVMs(它们是在服务器启动时注册的)上的每一个听者之中:

public void handleNotification(Notification notification, Object handback) {
System.out.println("Received alert: " + notification.getType());
//取得来自通知的事件userdata
Object userData = notification.getUserData();
if (userData instanceof UserWeb) {
 //来自于destin8 Web
 UserWeb WebVo = (UserWeb)userData;
 UserWebMBeanHelper helper = MBeanHelperFactory.getWebHelper();
 //使用MbeanHelper从值对象获取数据并放入本地MBean中
 helper.setAlertMessage(WebVo.getAlertMessage());
 helper.setAlertStatus(WebVo.getAlertStatus());
 helper.setCallBack(WebVo.getCallBack());
 helper.setRefreshAlertStatus(WebVo.getRefreshAlertStatus());
}
}

 "master" UserWeb数据被置入本地用户Web MBean中-经由它的MBean助理来实现。因而,每个管理服务器被用master UserWeb状态所更新。这就是JMX元素所具有的功能。

 七、 管理状态的AJAX请求

 要使浏览器客户端支持AJAX,需要具备如下:

 ·main.jsp-被装配的JSP页面,它能够检查(JMX)警告状态并向服务器查询警告。这个.jsp文件包括admin.js

 ·admin.js-这是一个JavaScript实用程序,它使用XMLHttpRequest来向服务器查询管理状态,分析XML响应,并且重画屏幕的"status"区域

 被包含在main.jsp中的JavaScript描述如下:

<script type="text/javascript" src="./js/admin.js" ></script>

 不是连续地查询,而是只有启动浏览器警告功能时我们才进行查询。我们使用UserWebMBeanHelper来检查这个功能。如果该功能被启动,那么当页面加载时,JavaScript函数initAdmin()将被调用:

<%
if (MBeanHelperFactory.getWebHelper().isAlertEnabled()) {
%>
<body bgcolor="#F4FFE4" onload="initAdmin();">
<%
} else {
%>
<body bgcolor="#F4FFE4">
<%
}
%>

 重画的'status'屏幕区域定义如下:

<span id="adminBanner" class="style1"></span>

 "adminBanner"将被使用来标记可重画的区域-当分析XML响应并提取消息时。

 这个initAdmin()方法调度一个JavaScript方法trapAlert()-这个方法在callbackTimeout毫秒后执行:

function setCallback() {
 callBack = setTimeout('trapAlert()',callbackTimeout);
}
function initAdmin() {
 setCallback();
}

 注意,是由trapAlert()方法来实现启动XMLHttpRequest:

function trapAlert() {
 if (window.XMLHttpRequest) {
  req = new XMLHttpRequest();
 } else if (window.ActiveXObject) {
  req = new ActiveXObject("Microsoft.XMLHTTP");
 }
 req.onreadystatechange = processRequest;
 req.open("GET", './admin?reqid=0', true);
 req.send(null);
}

 在此,HTTP GET用来读数据(只使用了一个小的请求参数),并且目标是admin servlet。这个请求是异步的,并且当请求状态变化时,processRequest JavaScript函数被调用:

req.onreadystatechange = processRequest;

 在继续处理前等待一个响应,这看上去似乎非常合理;然而,如果一个网络或服务器问题导致一个事务无法完成,那么你要冒着使你的脚本挂起来的危险。相比之下,一个相应于onreadystatechange事件的异步调用更为灵活些。

 在请求完成时,processRequest事件处理器被调用:

function processRequest() {
 if (req.readyState == 4) {
  if (req.status == 200) {
   parseMessages();
  }
  ....
  setCallback();//只有完成时才这样做
 }
}

 列表1(见下载源码)显示了所有可用的状态码。当请求完成并且返回HTTP状态代码200(OK)时,parseMessages()方法被调用以从XML消息中提取数据。然后,再次调度trapAlert()方法。如果XML响应有一个不同的重试间隔,那么这个值会由parseMessages()函数设置。
八、分析XML响应并重画屏幕

 parseMessages()函数首先提取XML响应

 response = req.responseXML;

 然后,它提取有关警告状态,警告文本和重试间隔等的元素:

itemStatus = response.getElementsByTagName('status')[0].firstChild.nodeValue;
itemText = response.getElementsByTagName('textBody')[0].firstChild.nodeValue;
callbackTimeout = parseInt(response.getElementsByTagName('callBack')[0].firstChild.nodeValue);

 然后,警告文本被重画到adminBanner文档元素(见上):

document.getElementById("adminBanner").innerHTML= itemText;

 该警告消息显示在如图3所示的屏幕上。

 图3 重画的屏幕

 九、Servlet格式化XML响应

 为了使浏览器把管理警告显示给用户,需要使用XMLHttpRequest来请求管理状态。

 当浏览器发送请求时,该servlet使用MBean助理来检查警告状态并且,如果一警告可用,即构建一个XML文档作为响应。

 如果没有返回状态,那么响应状态被设置如下:

response.setStatus(HttpServletResponse.SC_NO_CONTENT);

 否则,该文本/XML响应类型被设置为:

response.setContentType("text/xml");

 列表2显示了完整的servlet方法。

 当该servlet被调用并且返回XML内容时,控制台应该打印出:

Received alert: alert.broadcast
<message>
<status>1</status>
<textBody>
<![CDATA[System Down in 10 Minutes]]>
</textBody>
<callBack>10000</callBack>
</message>

 十、容量建模和安全性

 因为AJAX以有趣的方式开通了架构,所以存在两个关键方面要求加以考虑:

 ·容量建模

 ·安全性

 当然,缓冲和响应消息类型(XML或文本)也都是比较重要的。

 十一、容量建模

 支持AJAX的丰富的客户端不必再如以前那样频繁地提交请求。但是随着XMLHttpRequest异步地执行在浏览器端,向服务器发出的HTTP请求的数目也会相应于重试间隔而有所增加。

 ·再试间隔(思考时间)=20秒

 ·连接的用户数=5000

 ·事务每秒(TPS)=5000/20=250

 我们期望一个由HTTP用户基所产生的额外的每秒能够实现250次的请求(事务)。

 当然,这依赖于在服务器上的这些请求所完成的任务来提高响应时间上的潜力。在我们的实例中,每个请求必须查找MBean属性并且格式化一个XML响应,但是该响应很小而且MBean处理是在本地内存中。由于每个Web服务器线程每秒能够处理大约200个GET请求,以及用户横跨一个大约运行着200个线程的J2EE服务器来请求平衡加载,所以增加的加载并不太重要。

 还应注意,当建模AJAX架构时,增加的加载数可能随着带宽的减少而有所偏移。

 十二、安全性

 假定你只要求WebUser组中的用户能够存取该admin servlet,情况会怎样呢?

 如果仅是被认证的用户才能存取admin servlet,那么XMLHttpRequest将以该用户身份运行-如果该用户已经认证。

 例如,一旦用户Joe登录进这个应用程序,并且Joe是一个WebUser组的成员,那么XMLHttpRequest将能激活admin servlet。

 把下列代码添加到admin servlet将会确认被认证的主题,并分别返回true和Joe:

request.isUserInRole("WebUser");
request.getRemoteUser();

 十三、缓冲

 一些用户已经发现IE会缓冲来自AJAX请求的响应;这可能是由于浏览器/页面设置,但是一个强制性的解决办法是为该URL加上时间印戳:

var urlstr = "./admin?reqId=0&ts=" + new Date().getTimeStamp();

 十四、用不用XML?

 一些AJAX设计者欣然弃用XML而发送以普通文本形式的响应:

response.setContentType("text/plain");

 这明显要依赖于你的客户端需求和客户与所需求的数据之间的耦合程度。一个简单的文本响应对于一个文本警告就足够了;然而,本文中XML模型的优点在于,响应数据可以被进一步详细描述从而提炼状态和状态相关的数据。本文向你展示了怎样分析一更复杂的响应-客户可能必须编码以进行接收之。

 十五、 结论

 AJAX代表了一些新型的架构机会,然而它们不应该被丰富的客户端功能所遮蔽。本文在衡量了AJAX所提供的优点的同时,也强调了其对于容量和安全方面的技术要求-这是使用这种新型技术所必须要求的。

 

版权所有:UML软件工程组织