JMX MBean 基础知识
1 引言
这节课介绍JMX API的基本概念,即被管理的beans,或者叫MBeans。
一个MBean是一个被管理的Java对象,有点类似于JavaBean,它遵循了JMX规范的一些设计模式。一个设备、一个应用或者任何需要被管理的资源都可以被表示为MBean。MBean会暴露一个管理接口,这个接口主要包括如下几个操作:
- 一组可以读取或者写入,或既能读取也能写入的属性
- 一组触发读取或者写入的操作
- 一个自我描述
管理接口在MBean实例的生命周期内不会改变。当一些预定义的事件发生时,MBean也可以发送通知。
JMX API规范定义了5种类型的MBean:
- 标准MBean
- 动态MBean
- 开发MBean
- 模型MBean
- MXBean
接下来举几个例子来说明最简单类型的MBean,即标准MBean和MXBean。
2 标准MBean
这一节介绍一个简单的标准MBean的例子。
一个标准MBean这样定义的:编写一个叫做SomethingMBean的接口,然后编写一个叫做Something的类来实现这个接口。接口中的每一个方法要么定义了MBean的一个属性,要么定义了MBean的一个操作。默认情况下,每一个方法定义一个操作。
属性和操作都是遵循一定设计模式的方法。
一个标准MBean由一个MBean接口和实现了这个接口的类组成。MBean接口列出了所有需要暴露的属性和操作的方法。实现了这个接口的类提供了资源的功能。
接下来的小节举例说明如何通过标准MBean和简单的JMX代理来管理MBean。
2.1 MBean接口
下面是一个基本的MBean接口的例子,HelloBean:
按照惯例,一个MBean接口用一个实现它的类的名字加上后缀MBean命名。在上面的代码中,接口的名字为HelloBean。我们将在下一小节介绍实现了这个接口的Hello类。
根据JMX规范,除了具有名称和类型的操作(被MBean管理的应用可以触发这些操作)外,一个MBean接口还包含具有名称和类型的属性,这些属性是可读的,也有可能是可写的。HelloBean接口声明了两个操作:add()方法和sayHello()方法。
HelloBean声明了两个属性:name(一个可读的字符串),cacheSize(一个可读可写的整数)。Getter和Setter方法允许访问被管理的应用,也可能改变这些属性的值。正如JMX规范定义的那样,一个Getter方法是一个返回值不为空,以“get”开头命名的public方法。一个Getter方法使管理者能够读取属性的值,而属性的类型就是返回值的类型。一个Setter方法是一个只有一个输入参数,以“set”开头命名的public方法。一个Setter方法使管理者能够向属性中写入新值,而属性的类型就是输入参数的类型。
这些操作和属性的实现在下一节中。
2.2 MBean的实现
下面的Hello类实现了HelloMBean接口:
Hello类明确地定义了HelloMBean接口中声明的操作和属性。sayHello()和add()操作极其简单,但是实际中的操作根据需要既可以很简单也可以很复杂。
获取name属性的方法、获取cacheSize属性的方法、设置cacheSize属性的方法也都在Hello类中定义了。然而,在真实的场景中,被管理的资源在运行过程中,属性值可能会变化,例如那些表示开机时间或者内存使用的属性。
在这里,name属性的值仅仅是“Reginald”,不能改变。
cacheSize属性的默认值是200,可以调用setCacheSize方法改变这个属性。在真实的场景中,改变属性值需要执行一些其他的操作,例如丢弃条目或者分配新的条目。这个例子中仅仅打印了一条消息来确认cacheSize属性值已经改变了。然而,应该定义一些更加复杂操作,而不是简单的调用println()方法。
上面定义的Hello MBean以及它的接口,可以被用于管理它们所代表的资源了,就像下一节展示的那样。
2.3 创建JMX代理来管理资源
一旦某个资源被MBean监测,那么就可以通过JMX代理管理这个资源了。
JMX代理的核心组件是MBean服务器。MBean服务器是一个被管理对象的服务器,MBean被注册到了服务器。JMX代理也包含了一组服务来管理MBean。可以查看API文档了解MBeanServer接口及MBean服务器实现的详细信息。
下面所示的Main类表示一个基本的JMX代理:
首先,JMX代理类Main调用java.lang.management.ManagementFactory类的getPlatformMBeanServer()方法从平台获取一个MBean服务器,该服务器由平台负责创建并初始化。如果平台还没有创建MBean服务器,那么getPlatformMBeanServer()方法将自动调用MBeanServerFactory.createMBeanServer()方法创建一个MBean服务器。Main类获取的MBean服务器实例的名字为mbs。
下一步,Main类定义了一个MBean实例的对象名。每个JMX MBean必须有一个对象名。对象名是JMX类ObjectName的实例,必须遵守JMX规范定义的语法。即对象名必须包含一个域和一组键属性。在Main类中定义的对象名,域是com.example(就是包含MBean的包名),另外,键属性声明了这个对象的类型为Hello。
然后,创建一个Hello实例对象mbean,连同对象名一起作为MBean注册到MBean服务器mbs中。通过调用JMX方法 MBeanServer.registerMBean(),并将对象及对象名作为入参传递给该方法实现注册。
Hello MBean注册到MBean服务器后,Main类只是简单地等待Hello里面的管理操作被执行。在这个例子里面,这些管理操作主要是sayHello()和add()方法,以及属性的get和set方法。
2.4 运行这个标准MBean的例子
如果已经检查过例子中的类,你现在就可以运行这个例子了。在这个例子中,使用jconsole与MBean进行交互。
- 保存JMX例子的压缩包jmx_examples.zip到你的工作目录work_dir。
- 在命令行或者终端使用下面的命令解压缩这个包:unzip jmx_examples.zip。
- 在work_dir目录下编译例子中所有类:javac com/example/*.java。
- 如果你运行的是JDK6,就用下面的命令行启动Main应用:java com.example.Main
如果你运行的是JDK6以前的版本,你需要使用下面指定的可选参数启动Main应用,并暴漏应用给以便监控和管理:java -Dcom.sun.management.jmxremote example.Main
确认显示Main正在等待一些事情发生。
- 在同一台机器上打开一个新的命令行或者终端窗口,输入命令jconsole启动jconsole
- 在新建连接对话框中,从列表中选择com.example.Main中,并点击connect。这时,平台的当前活动的概述将会显示出来。
- 点击MBean标签,这个面板将会显示所有的已经注册到MBean服务器中的MBean。
- 在左边的MBean树中,展开com.example节点。你会看到在Main创建并注册的MBean Hello。如果你点击Hello,你会在MBean树中看到它关联的“属性”和“操作”节点。
- 在MBean树中,展开Hello MBean的“属性”节点。在Hello中定义的MBean属性就会显示出来。
- 将cacheSize属性的值改为150。在启动Main的命令行或者终端窗口,可以确认属性已经修改。
- 在MBean树中,展开Hello MBean的“操作”节点。可以看到两个在Hello MBean中声明的操作:sayHello()和add()。
- 点击sayHello按钮,触发sayHello()操作。JConsole对话框会通知你sayHello方法调用成功。在运行Main类的命令行窗口产生了“hello, world”消息。
- 给add()操作提供两个整数,并点击add按钮。在JConsole对话框中将会显示出答案。
- 要想关闭JConsole,选择 连接->退出。
3 MXBean
这一节解释一个特殊的MBean:MXBean。
MXBean也是MBean,只是它引用了一些预定义的数据类型。这样你可以确保你的MBean可以被任何MBean客户端使用,包括远程客户端。不需要额外的要求,任何客户端都有权访问代表你的MBean的造型特异的类。MXBean提供了一个便捷的方式将相关的值捆绑在一起,而不需要客户端有特殊的配置就能处理这个bundle。
用与标准MBean相同的方式,MXBean是这样定义的:编一个接口叫SomethingMXBean,并编写一个类来实现这个接口。然而,不像标准MBean,MXBean不需要这个类的名字必须为Something。接口中的每一个方法要么定义了MXBean的一个属性,要么定义了MXBean的一个操作。注解@MXBean可以用于标注这个接口,而不需要这个接口的名字的后缀必须为MXBean。
MXBean存在于Java2平台标准版J2SE5.0的包java.lang.management中。然而,除了在java.lang.management包中定义的标准MXBean,用户可以定义自己的MXBean。
MXBean的主要思想是一些类型,例如java.lang.management.MemoryUsage,被MXBean接口java.lang.management.MemoryMXBean引用。在这种情况下,在包javax.management.openmbean定义的所谓开放类型(Open Types)可以被映射到标准类型集上。精确的映射规则在MXBean规范中。然而,一般原则是简单数据类型(例如int,String)保持不变,而复杂数据类型(例如MemoryUsage)则映射到标准类型CompositeDataSupport。
MXBean的示例主要包含下面几个文件,可以在jmx_examples.zip中找到:
- QueueSamplerMXBean:接口
- QueueSampler:实现了MXBean接口的类
- QueueSample:MXBean接口中的getQueueSample()方法的返回值类型
- Main:设置和运行示例的程序。
MXBean示例用这几个类完成下面的几个动作:
- 定义一个简单的MXBean来管理Queue<String>类型的资源。
- MXBean中声明了一个get方法getQueueSample(),当触发该方法时可以获得队列的快照,返回值为QueueSample类型,它捆绑了下面这些值:快照生产的时间,队列大小,指定时间队列的头。
- 将MXBean注册到MBean服务器。
3.1 MXBean接口
下面的代码展示了MXBean接口QueueSamplerMXBean:
注意:声明MXBean接口和声明MBean接口的方式是完全一样的。QueueSamplerMXBean声明了一个get方法getQueueSample和一个操作clearQueue。
3.2 定义MXBean操作
MXBean操作是在QueueSampler类中定义的,如下所示:
QueueSampler定义了在MXBean接口中声明的getQueueSample()方法和clearQueue()操作。getQueueSample()操作返回一个QueueSample类的实例,这个实例由java.util.Queue类的peek()方法返回值和size()方法返回值以及java.util.Date类实例构造。
3.3 定义MXBean接口中的返回值类型
QueueSampler类中返回的QueueSample实例定义在QueueSample类中,如下所示:
在QueueSample类中,MXBean框架调用QueueSample中所有的get方法,将给定的实例转换成一个CompositeData instance实例,使用@ConstructorProperties注解从CompositeData实例中重构出QueueSample实例。
3.4 创建MXBean并将其注册到MBean服务器中
到目前为止,我们已经定义了下面几个类:一个MXBean接口,一个实现了该接口的类,还有一个返回值的类型。下一步,必须创建一个MXBean,并将其注册到MBean服务器中。这些动作也是由JMX代理Main类来执行,同标准MBean示例一样,但是相关的代码没有在标准示例中显示。
Main类执行了下面几个动作:
- 获取平台MBean服务器
- 创建MXBean QueueSampler的对象名
- 创建QueueSampler MXBean要处理的Queue实例
- 用Queue实例创建一个QueueSampler MXBean实例
- 将这个MXBean实例注册到MBean服务器(用于标准MBean一样的方式)
3.5 运行MXBean示例
MXBean示例使用jmx_examples.zip包里面的类,同标准MBean那一节是一样的。这个例子需要Java SE6以上版本。按照下面的步骤运行MXBean示例:
- 如果这一步还有做,那就保存JMX例子的压缩包jmx_examples.zip到你的工作目录work_dir。
- 在命令行或者终端使用下面的命令解压缩这个包:unzip jmx_examples.zip
- 在work_dir目录下编译例子中所有类:javac com/example/*.java
- 启动Main应用:java com.example.Main。确认显示Main正在等待一些事情发生。
- 在同一台机器上打开一个新的命令行或者终端窗口,启动JConsole。新建连接对话框将会显示出来,列表中显示出可以连接的正在运行的JMX代理。
- 在新建连接对话框中,从列表中选择com.example.Main中,并点击connect。这时,平台的当前活动的概述将会显示出来。
- 点击MBean标签,这个面板将会显示所有当前注册到MBean服务器中的MBean。
- 在左边的MBean树中,展开com.example节点。你会看到在Main创建并注册的MBean QueueSampler。如果你点击QueueSampler,你会在MBean树中看到它关联的“属性”和“操作”节点。
- 展开“属性”节点。你将会看到属性QueueSample出现在右边的面板,还有它的值javax.management.openmbean.CompositeDataSupport。
- 双击CompositeDataSupport值。你会看到QueueSample的date、head和size属性值。因为MXBean框架已经将QueueSample实例转换成了CompositeData。假设你定义的QueueSampler是标准的MBean而不是MXBean,那么JConsole就找不到QueueSample类,因为它不在类路径里。如果QueueSampler是一个标准的MBean,那么当你恢复QueueSample属性值时,你会收到一个ClassNotFoundException异常消息。JConsole能够找到QueueSampler类的事实说明:当通过通用JMX客户端(例如JConsole)连接JMX代理时,使用MXBean是很有用的。
- 展开“操作”节点。触发clearQueue操作的按钮就会显示出来。
- 点击clearQueue按钮。就会显示触发方法成功的确认消息。
- 再次展开“属性”节点,双击CompositeDataSupport值。你会看到,Head和size的值已经被重置。
- 要想关闭JConsole,选择 连接->退出。