用于管理Java应用程序的JMX【转】
网络和应用程序管理是个错误的名称。当供应商沿街叫卖他们的应用程序管理软件时,他们真正卖的其实是应用程序监控软件。尽管几乎所有的应用程序管理产品都观测网络应用程序的状态和行为,但在对观测结果采取相应措施方面却没有提供什么措施。当我想要管理一些东西,我希望能采取一些措施。例如 , 配置管理并不仅仅是观测改变并对其进行记录。那将是修正性的控制。配置管理将自动重新配置系统,使其与具体化的环境一致,解决对其他特定版本系统的依靠性问题等等。管理将为您做一些事情,而不仅仅是通知您系统状态已经发生改变,然后就将采取相应措施的责任推到您身上 。
让我们来看看 Java Management Extension (JMX) 是真正的管理扩展抑或仅仅是监控扩展。当开始使用 JMX ,并精读 JSR-3 规范,您不会发现 管理 和 监控 这样显示定义的术语。同样,您将有这样一种总体感觉,即规范将管理和监控识别为两个独立的活动。监控通过报告系统状态支持管理活动。管理则允许您对系统状态进行更改,通常更改是对监控结果的一种响应。
JMX 提供的接口标准集使您能够为您的 Java 应用程序添加管理和监控能力,同时构建管理和监控应用程序。 JMX 同时定义了划分为 4 部分的管理架构:实现、代理服务、管理协议 API 和分布式服务。前三个组件在当前 1.2 规范中定义,第 4 个组件将在规范的后续版本中定义。 实现层提供向代理层公开可管理资源的方法。它允许代理查询资源状态并调用对这些资源公开的操作。同样,它允许资源将事件发送到管理架构的其他部分。 代理层允许您实现控制实现层资源的管理代理。代理是位于资源和分布式服务层的远程管理应用程序之间的中间人。管理协议将两层链接在一起。 分布式服务层包括应用程序连接器和协议适配器,以及所有重要的应用程序级的管理支持。正是这最后一层将允许您编写管理应用程序,应用程序将对改变的环境采取相应措施并指导管理代理执行诸如复制或改变资源分配策略之类的任务。如果给定 JMX 架构中该层的未定义状态,说 JMX 并没有提供完整的管理能力并非一种失之公平的说法。 Mbeans 的 4 种类型 JMX 实现层的核心组件是托管 bean 或 Mbean 。 Mbean 是代表将被管理的资源的 Java 对象。 Mbean 本身并不需要是资源,但是它必须公开资源的属性和操作。例如 , Mbean 也许将实现返回服务器的正常运行时间和系统负载的方法。 JMX 定义了 4 种类型的 Mbeans :标准型、动态型、开放型和模型型。 标准型 Mbean 是一个 Java 类,它实现与后缀为 Mbean 的类有相同名字的接口。所以如果您想要一个名为 TemperatureSensor 的类成为一个标准的 Mbean ,您只需定义一个名为 TemperatureSensorMBean 的、由 TemperatureSensor 实现的接口。代理层将通过反射来判定一个类是否为 Mbean ,并发现它的属性和操作。 Mbean 属性与 setter 和 getter 方法(如 setTemperature() 和 getTemperature() )以及布尔型 is 方法(如 isOverheating() )相对应。在 Mbean 接口中所有其他的方法都当作操作来解释,如 shutdownSystem() 。这些命名方法的规则和 JavaBeans 中要求的规则相似。 SystemTime 类和 SystemTimeMBean 接口(参见 清单 1 )一起实现了标准的 Mbean 。 标准 MBean 清单 1. 标准的 Mbean 实现了名字以 Mbean 结束的接口 package example; 丢失 Mbean 清单 2. 标准 Mbeans 同样能够通过编写 StandardMBean 的子类和实现任意指定的接口(接口向 StandardMBean 构建函数提供其类对象)的方法来实现。 package example; 您能够通过创建实现 DynamicMBean 接口的类来实现动态 Mbean 。 DynamicMBean 声明了 getAttribute() 、 setAttribute() 、 getAttributes() 、 setAttributes() 和 getMBeanInfo() 的调用方法。属性方法设置并返回您希望的属性值。调用方法用于执行操作,并能通过使用反射(正如通过 StandardBean 完成的一样,实际上实现了 DynamicMBean )和将操作名转换成使用条件语句的方法调用或其他供您选择的方法调用来实现。 getMBeanInfo() 方法应当返回对 Mbean 的属性和操作进行描述的 MbeanInfo 实例。 开放型 MBeans 和模型型 MBeans 都是动态 Mbeans 的具体类型。开放型 Mbean 只是一种动态 Mbean ,它把对数据类型的使用限制为在 JMX 规范中定义的受限集。开放型 Mbeans 能够在任何 JMX 环境中操作,因为它们不需要特别的类或其他支持。模型型 Mbean 是一种动态 Mbean ,实现了 ModelBean 接口,并由在 ModelBeanInfo 对象中提供元数据的资源配置。模型型 beans 本身并非资源,而是提供将资源按照类属适配到 Mbean 接口的能力。模型型 Mbeans 需要实现 NotificationBroadcaster 接口,它允许代理注册并接受与资源状态改变相关的事件。 代理和缺乏管理 因为 Mbeans 对资源进行封装,所以代理在那些封装的资源和管理应用程序之间进行调节。代理通常与它们所调节的资源位于同一个组中,虽然并不需要位于同一个地方。例如,一个单独的代理也许运行在服务器上,对封装有资源(资源报告 CPU 状态、磁盘性能、内存使用情况、数据库吞吐量和其他属性)的 Mbeans 进行调节。 代理可以有很多种形式。它们可以是嵌入式组件、单独的服务甚至 Mbeans 。不管它采取什么形式 , JMX 代理必须对跟踪 Mbeans 的 Mbean 服务器拥有访问权。 Mbean 服务器提供的功能由 MbeanServer 接口定义,该接口起到 Mbean 注册的作用。 SimpleAgent ( 参见 清单 3 )说明了代理怎样通过 MbeanServer 接口来访问 Mbeans 。 简单代理 清单 3. 代理通过 MbeanServer 类与 Mbeans 交互 import java.util.*; SimpleAgent 并不是真正的 JMX 代理,它实际是创建一个 MbeanServer 实例来注册一组 Mbeans 的程序示例。真正的 JMX 代理将为外部程序提供与 Mbeans 交互的方法。 MBeanServerFactory 为给定的 JMX 环境创建默认 MbeanServer 示例。一旦拥有 MbeanServer 实例后,您就能够通过 registerMBean 方法来注册现有的 Mbeans ,或者使 MbeanServer 通过 createMBean 创建新的实例(就像在 SimpleAgent.createMBean 中的做法一样)。每个 Mbean 属于一个指定的管理域,并在该管理域中具有唯一的名字。每个实例还有一个与之关联的属性列表,这样就不至于同 Mbean 的属性混淆。实例属性有助于将属于同一类型的 Mbeans 区别开来。 此刻, JMX 对于应用程序管理供应商而言比对普通企业应用程序开发人员更具吸引力。不具有非分布式服务层,您就不能通过 JMX 编写应用程序管理逻辑。虽然有些情况下您想要创建自己的 Mbeans 和代理,但管理供应商极有可能已经提供了很多 Mbeans 和代理,如:用于数据库的代理和 Mbeans 、 Web 服务器、操作系统、网络交换机及其他企业入籍权。定义应用程序管理的是:您怎样将所有这些资源捆绑在一起,以及对怎样根据改变的条件来相应地改变资源的关系和行为作出决定。作出决定和采取措施才是管理的真谛,但在 JMS 在这方面帮助您之前,您将必须等待。 原文出处 http://www.fawcette.com/javapro/2003_10/magazine/columns/proshop/
import java.util.*;
public interface SystemTimeMBean {
public Date getTime();
}
package example;
import java.util.*;
public class SystemTime implements SystemTimeMBean {
public Date getTime() {
TimeZone zone = TimeZone.getTimeZone(“GMT+0″);
Calendar calendar = Calendar.getInstance(zone);
return calendar.getTime();
}
}
JMX 1.2 添加了定义标准 Mbeans 的第二种方法。要求标准 Mbeans 实现通过特定方法指定的接口可能会很受限。因此 , 现在能够通过编写 StandardMBean 类的子类和实现任意指定的接口的方法来实现标准 Mbeans 。接口的方法仍然根据 Mbean 方法命名规约来解释,但是接口名不必再以 Mbean 后缀结尾。 Mbean 必须将接口类对象提供给 StandardMBean 构建函数,这样才能对接口方法进行确定。还可以将已有的对象包装起来,并将对象和接口都提供给 StandardMBean 构建函数。例如 , Clock 接口和 ClockImpl 类(参见 清单 2 )通过从 javax.management 程序包中编写 StandardMBean 的子类实现了标准的 Mbean 。不是通过编写 StandardMBean 的子类, ClockImpl 也可以定义为根类来实现 Clock 接口。在那种情况下,您可通过以下方法创建标准 Mbean :
import java.util.*;
public interface Clock {
public Date getTime();
}
package example;
import java.util.*;
import javax.management.*;
public class ClockImpl extends StandardMBean
implements Clock {
public ClockImpl() throws NotCompliantMBeanException
{
super(Clock.class);
}
public Date getTime() {
TimeZone zone = TimeZone.getTimeZone(“GMT+0″);
Calendar calendar = Calendar.getInstance(zone);
return calendar.getTime();
}
}
标准 Mbean 的属性和操作是静态的。它们由必须预先定义的固定接口来定义。动态 Mbeans 可在运行时定义属性和操作。这
能力允许 Mbean 在装载时动态配置自己或根据它所处环境改变它的属性和操作。例如 , LocationSensor Mbean 能够根据独立的雇员进入或离开它的邻近范围的情况,动态添加或删除属性。
import javax.management.*;
public class SimpleAgent {
private MBeanServer server;
private ArrayList mbeanNames;
public SimpleAgent() {
server = MBeanServerFactory.createMBeanServer();
mbeanNames = new ArrayList();
}
public void createMBean(String className,
String key, String value)
throws JMException
{
ObjectName objectName =
new ObjectName(server.getDefaultDomain(),
key, value);
mbeanNames.add(objectName);
server.createMBean(className, objectName);
}
public void manage() throws JMException {
Iterator it = mbeanNames.iterator();
while(it.hasNext()) {
ObjectName oname;
MBeanInfo mbinfo;
MBeanAttributeInfo[] attinfo;
oname = (ObjectName)it.next();
mbinfo = server.getMBeanInfo(oname);
attinfo = mbinfo.getAttributes();
System.out.println(oname.toString());
for(int i=0; i < attinfo.length; ++i) {
String attname = attinfo[i].getName();
Object attval = server.getAttribute(
oname, attname);
System.out.println(
attname + ” : ” + attval.toString());
}
System.out.println();
try {
Thread.sleep(1234);
} catch(InterruptedException e) {
continue;
}
}
}
public static final void main(String[] args) {
SimpleAgent agent = new SimpleAgent();
try {
agent.createMBean(“example.SystemTime”, “name”,
“SystemTime”);
agent.createMBean(“example.ClockImpl”, “name”,
“ClockImpl”);
agent.manage();
} catch(Throwable t) {
t.printStackTrace();
return;
}
}
}