JMX
主要分为五类:
- Standard MBeans
- Dynamic MBeans
- Open MBeans
- Model MBeans
- MXBeans
Standard MBeans
Standard MBean 一般用于管理相对比较稳定的资源。一般对于Standard MBean,我们会使用显式声明的管理接口(我们自己定义的),这些接口一旦创建之后就不再改变。
package com.example;
public interface HelloMBean {
public void sayHello();
public int add(int x, int y);
public String getName();
public int getCacheSize();
public void setCacheSize(int size);
}
public class Hello implements HelloMBean {
public void sayHello() {
System.out.println("hello, world");
}
public int add(int x, int y) {
return x + y;
}
public String getName() {
return this.name;
}
public int getCacheSize() {
return this.cacheSize;
}
public synchronized void setCacheSize(int size) {
this.cacheSize = size;
System.out.println("Cache size now " + this.cacheSize);
}
...
private final String name = "Reginald";
private int cacheSize = DEFAULT_CACHE_SIZE;
private static final int
DEFAULT_CACHE_SIZE = 200;
}
package com.example;
import java.lang.management.*;
import javax.management.*;
public class Main {
public static void main(String[] args)
throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=Hello");
Hello mbean = new Hello();
mbs.registerMBean(mbean, name);
System.out.println("Waiting forever...");
Thread.sleep(Long.MAX_VALUE);
}
}
可以通过页面或者jconsole等工具改变属性值
Dynamic MBeans
DynamicMBean是在运行期才定义它的属性和方法,也就是说它有什么属性和方法是可以动态改变的。动态MBean主要利用一些辅助类(构造函数类MBeanConstructorInfo、属性类MBeanAttributeInfo、方法类MBeanOperationInfo)来完成这个功能,所有的DynamicMBean必须实现DynamicMBean接口。DynamicMBean写好后,使用方法和第一篇文章中普通的MBean一样。
package com.util.concurrent;
import java.lang.reflect.Constructor;
import java.util.Iterator;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.DynamicMBean;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ReflectionException;
public class HelloDynamic implements DynamicMBean {
//这是我们的属性名称
private String name;
private MBeanInfo mBeanInfo = null;
private String test;
private String className;
private String description;
private MBeanAttributeInfo[] attributes;
private MBeanConstructorInfo[] constructors;
private MBeanOperationInfo[] operations;
MBeanNotificationInfo[] mBeanNotificationInfoArray;
public HelloDynamic() {
init();
buildDynamicMBean();
}
private void init() {
className = this.getClass().getName();
description = "Simple implementation of a dynamic MBean.";
attributes = new MBeanAttributeInfo[2];
constructors = new MBeanConstructorInfo[1];
operations = new MBeanOperationInfo[1];
mBeanNotificationInfoArray = new MBeanNotificationInfo[0];
}
private void buildDynamicMBean() {
//设定构造函数
Constructor[] thisconstructors = this.getClass().getConstructors();
constructors[0] = new MBeanConstructorInfo("HelloDynamic(): Constructs a HelloDynamic object", thisconstructors[0]);
//设定一个属性
attributes[0] = new MBeanAttributeInfo("Name", "java.lang.String", "Name: name string.", true, true, false);
attributes[1] = new MBeanAttributeInfo("Test", "java.lang.String", "Name: name string.", true, true, false);
//operate method 我们的操作方法是print
MBeanParameterInfo[] params = null;//无参数
operations[0] = new MBeanOperationInfo("print", "print(): print the name", params, "void", MBeanOperationInfo.INFO);
mBeanInfo = new MBeanInfo(className, description, attributes, constructors, operations, mBeanNotificationInfoArray);
}
//动态增加一个print1方法
private void dynamicAddOperation() {
init();
operations = new MBeanOperationInfo[2];//设定数组为两个
buildDynamicMBean();
operations[1] = new MBeanOperationInfo("print1", "print1(): print the name", null, "void", MBeanOperationInfo.INFO);
mBeanInfo = new MBeanInfo(className, description, attributes, constructors, operations, mBeanNotificationInfoArray);
}
public Object getAttribute(String attribute_name) {
if (attribute_name != null)
return null;
if (attribute_name.equals("Name"))
return name;
if (attribute_name.equals("Test"))
return test;
return null;
}
public void setAttribute(Attribute attribute) {
if (attribute == null)
return;
String Name = attribute.getName();
Object value = attribute.getValue();
try {
if (Name.equals("Name")) {
// if null value, try and see if the setter returns any exception
if (value == null) {
name = null;
// if non null value, make sure it is assignable to the attribute
} else if ((Class.forName("java.lang.String")).isAssignableFrom(value.getClass())) {
name = (String) value;
}
}
if (Name.equals("Test")) {
// if null value, try and see if the setter returns any exception
if (value == null) {
test = null;
// if non null value, make sure it is assignable to the attribute
} else if ((Class.forName("java.lang.String")).isAssignableFrom(value.getClass())) {
test = (String) value;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public AttributeList getAttributes(String[] attributeNames) {
if (attributeNames == null)
return null;
AttributeList resultList = new AttributeList();
// if attributeNames is empty, return an empty result list
if (attributeNames.length == 0)
return resultList;
for (int i = 0; i < attributeNames.length; i++) {
try {
Object value = getAttribute(attributeNames[i]);
resultList.add(new Attribute(attributeNames[i], value));
} catch (Exception e) {
e.printStackTrace();
}
}
return resultList;
}
public AttributeList setAttributes(AttributeList attributes) {
if (attributes == null)
return null;
AttributeList resultList = new AttributeList();
// if attributeNames is empty, nothing more to do
if (attributes.isEmpty())
return resultList;
// for each attribute, try to set it and add to the result list if successfull
for (Iterator i = attributes.iterator(); i.hasNext();) {
Attribute attr = (Attribute) i.next();
try {
setAttribute(attr);
String name = attr.getName();
Object value = getAttribute(name);
resultList.add(new Attribute(name, value));
} catch (Exception e) {
e.printStackTrace();
}
}
return resultList;
}
public Object invoke(String operationName, Object params[], String signature[]) throws MBeanException, ReflectionException {
// Check for a recognized operation name and call the corresponding operation
if (operationName.equals("print")) {
//具体实现我们的操作方法print
System.out.println("Hello, " + name + ", this is HellDynamic!");
System.out.println("Hello, " + test + ", this is HellDynamic!");
dynamicAddOperation();
return null;
} else if (operationName.equals("print1")) {
System.out.println("这是动态增加的一方法print1");
return null;
} else {
// unrecognized operation name:
throw new ReflectionException(new NoSuchMethodException(operationName), "Cannot find the operation " + operationName + " in " + className);
}
}
public MBeanInfo getMBeanInfo() {
return mBeanInfo;
}
}
package com.util.concurrent;
import com.sun.jdmk.comm.HtmlAdaptorServer;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
public class HelloAgent {
public static void main(String[] args) throws Exception {
MBeanServer server = MBeanServerFactory.createMBeanServer();
ObjectName helloName = new ObjectName("chengang:name=HelloDynamic");
HelloDynamic hello = new HelloDynamic();
server.registerMBean(hello, helloName);
ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082");
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
server.registerMBean(adapter, adapterName);
adapter.start();
System.out.println("start.....");
}
}
其中的一种玩法
package com.test.jmx.modeler;
public class Hello{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name= name;
}
public void printHello() {
System.out.println("Hello World, "+name);
}
public void printHello(String whoName) {
System.out.println("Hello, "+whoName);
}
}
接下去就是最关键的描述文件(mbeans-descriptors.xml)了:
<?xml version="1.0" encoding="UTF-8" ?>
<mbeans-descriptors>
<mbean name="Hello" description="the hello bean" domain="MyMBean" group="helloGroup" type="com.test.jmx.modeler.Hello">
<attribute name="name" description="a name attribute" type="java.lang.String" writeable="true"/>
<operation name="printHello" description="public void printHello()" impact="ACTION" returnType="void"/>
<operation name="printHello" description="public void printHello(String whoName)" impact="ACTION" returnType="void">
<parameter name="whoName" description="method parameter of printHello" type="java.lang.String"></parameter>
</operation>
</mbean>
</mbeans-descriptors>
描述文件的名字可以随意,最主要的是要和下面的HelloAgent.java对应起来。
通过这个xml文件的定义就描述了Model MBean所需要的metadata信息和一个基本的ModelMBean实现。
关于这个xml文件有几个需要说明的地方:
的属性classname,name,type:
- name属性是每个MBean被Registry对象注册的对象名
- type属性是真正被管理资源的全面(包括包名)
- classname属性是用户扩展的用于实现代理功能的Model MBean的全名,如果不提供Modeler会使用
- BaseModelMBean;如果提供了代理的ModelMBean对象,在使用时可以使用如下的代码样本访问他所代理的资源对象。
其余的标签就比较好理解了。综述:上面所示代码声明了一个Model MBean, 唯一标示是“Hello”,该MBean负责管理的对象是com.test.jmx.modeler.Hello的实例。域是MyMBean。这个MBean暴露了一个属性name和两个方法printHello()和printHello(String whoName).
下面是新的HelloAgent.java的代码:
package com.test.jmx.modeler;
import com.sun.jdmk.comm.HtmlAdaptorServer;
import org.apache.commons.modeler.ManagedBean;
import org.apache.commons.modeler.Registry;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBean;
import java.io.InputStream;
public class HelloAgent {
public static void main(String[] args) throws Exception {
// 需要将xml信息读入到Registry对象中
Registry registry = Registry.getRegistry(null,null);
InputStream stream = HelloAgent.class.getResourceAsStream("mbeans-descriptors.xml");
registry.loadMetadata(stream);
MBeanServer server = registry.getMBeanServer();
// 之前是:MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ManagedBean managed = registry.findManagedBean("Hello");
ObjectName helloName = new ObjectName(managed.getDomain()+":name=HelloWorld");
// 以前是Hello hello = new Hello(); 为什么要多个createMBean?因为现在的写法没有写MBean,所以才要动态生成一个,以前就直接
// 把new hello()注册到MBeanServer就可以了,server会自动找到它的HelloMBean接口文件。
ModelMBean hello = managed.createMBean(new Hello());
server.registerMBean(hello,helloName);
ObjectName adapterName = new ObjectName(managed.getDomain()+":name = htmladapter,port=8082");
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
server.registerMBean(adapter,adapterName);
adapter.start();
}
}
注意这里的Registry是指org.apache.commons.modeler.Registry,因为JMX自身也有一个Registry(java.rmi.registry.Registry)。通过Modeler组件提供的Registry对象,可以很方便的完成MBeanServer的创建。
Open MBeans
Open MBeans被设计为可以被更大范围的管理程序访问.
Open MBean使用通用的数据类型。 管理程序和Open MBeans就可以共享和使用数据和方法,而不需要重新编译,重新组配或者动态链接。所以说,Open MBeans提高了管理系统的灵活性和可扩展性。
Open MBeans当管理程序无法访问agent的java classes的时候,就特别有用。即使管理程序和agent之间的连接不支持java序列化的时候,也是可以访问Open MBeans的。比如说,管理程序不是java开发的。
Open MBeans必须实现DynamicMBean接口,以对管理程序提供自我描述。与DynamicMBean的不同,就是Open MBean提供了更复杂的metadata数据,和在接口中,只使用了几种预定义的数据类型。
判断一个MBean是不是open的,可以根据它返回的MBeanInfo对象来判断。Open MBean返回的是OpenMBeanInfo对象,这个类是MBeanInfo的子类。
Model MBean
相对于Standard MBean,Model MBean更加灵活。如果我们不能修改已有的Java类,那么使用Model MBean是不错的选择。
Model MBean也是一种专门化的动态管理构件。它是预制的、通用的和动态的 MBean 类,已经包含了所有必要缺省行为的实现,并允许在运行时添加或覆盖需要定制的那些实现。JMX规范规定该类必须实现为javax.management.modelmbean.RequiredModelMBean,管理者要做的就是实例化该类,并配置该构件的默认行为并注册到JMX代理中,即可实现对资源的管理。JMX代理通过获得一个ModelMBeanInfo对象来获取管理接口。
模型管理构件具有以下新的特点:
- 持久性。定义了持久机制,可以利用Java的序列化或JDBC来存储模型MBean的状态。 就是要保存到硬盘上。
- 通知和日志功能。能记录每一个发出的通知,并能自动发出属性变化通知。
- 属性值缓存。具有缓存属性值的能力。
package com.test.jmx.modelBean;
public class Hello { //注意这里没有implements任何东西
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printHello(){
System.out.println("Hello world, "+name);
}
public void printHello(String whoName){
System.out.println("Hello, "+whoName);
}
}
package com.test.jmx.modelBean;
import javax.management.*;
import javax.management.modelmbean.*;
public class ModelMBeanUtils {
private static final boolean READABLE = true;
private static final boolean WRITABLE = true;
private static final boolean BOOLEAN = true;
private static final String STRING_CLASS = "java.lang.String";
public static RequiredModelMBean createModelerMBean() {
RequiredModelMBean model = null;
try {
model = new RequiredModelMBean();
model.setManagedResource(new Hello(), "ObjectReference");
ModelMBeanInfo info = createModelMBeanInfo();
model.setModelMBeanInfo(info);
} catch (Exception e) {
e.printStackTrace();
}
return model;
}
private static ModelMBeanInfo createModelMBeanInfo() {
//
// 属性 //
//
// 构造name属性信息
Descriptor portAttrDesc = new DescriptorSupport();
portAttrDesc.setField("name", "Name");
portAttrDesc.setField("descriptorType", "attribute");
portAttrDesc.setField("displayName", "Name");
portAttrDesc.setField("getMethod", "getName");
portAttrDesc.setField("setMethod", "setName");
ModelMBeanAttributeInfo nameAttrInfo = new ModelMBeanAttributeInfo(//
"Name", // 属性名
STRING_CLASS, //属性类型
"people name", // 描述文字
READABLE, WRITABLE, !BOOLEAN, // 读写
portAttrDesc // 属性描述
);
//
// 方法 //
//
// 构造 getName操作描述符信息
Descriptor getStateDesc = new DescriptorSupport(new String[] {
"name=getName",
"descriptorType=operation",
"class=com.test.jmx.modelBean.Hello",
"role=operation"
});
ModelMBeanOperationInfo getName = new ModelMBeanOperationInfo(//
"getName", //
"get name attribute", //
null, //
"java.lang.String", //
MBeanOperationInfo.ACTION, //
getStateDesc //
);
// 构造 setName操作描述符信息
Descriptor setStateDesc = new DescriptorSupport(new String[] {
"name=setName", "descriptorType=operation", "class=com.test.jmx.modelBean.Hello",
"role=operation" });
MBeanParameterInfo[] setStateParms = new MBeanParameterInfo[] { (new MBeanParameterInfo(
"name", "java.lang.String", "new name value")) };
ModelMBeanOperationInfo setName = new ModelMBeanOperationInfo(//
"setName", //
"set name attribute", //
setStateParms, //
"void", //
MBeanOperationInfo.ACTION, //
setStateDesc //
);
//构造 printHello()操作的信息
ModelMBeanOperationInfo print1Info = new ModelMBeanOperationInfo(//
"printHello", //
null, //
null, //
"void", //
MBeanOperationInfo.INFO, //
null //
);
// 构造printHello(String whoName)操作信息
ModelMBeanOperationInfo print2Info;
MBeanParameterInfo[] param2 = new MBeanParameterInfo[1];
param2[0] = new MBeanParameterInfo("whoName", STRING_CLASS, "say hello to who");
print2Info = new ModelMBeanOperationInfo(//
"printHello", //
null,//
param2,//
"void", //
MBeanOperationInfo.INFO, //
null//
);
//
// 最后总合 //
//
// create ModelMBeanInfo
ModelMBeanInfo mbeanInfo = new ModelMBeanInfoSupport(//
RequiredModelMBean.class.getName(), // MBean类
null, // 描述文字
new ModelMBeanAttributeInfo[] { // 所有的属性信息(数组)
nameAttrInfo },//只有一个属性
null, // 所有的构造函数信息
new ModelMBeanOperationInfo[] { // 所有的操作信息(数组)
getName,
setName,
print1Info,
print2Info },//
null, // 所有的通知信息(本例无)
null//MBean描述
);
return mbeanInfo;
}
}
这里着重说明下ModelMBeanInfo接口
编写Model MBean的最大挑战是告诉Model MBean对象托管资源的那些熟悉和方法可以暴露给代理。ModelMBeanInfo对象描述了将会暴露给代理的构造函数、属性、操作甚至是监听器。
创建了ModelMBeanInfo对象后,需要将其与ModelMBean对象关联。目前有两种方式可以做到这一点:
传入ModelMBeanInfo对象给RequiredModelMBean对象的构造函数。
调用RequiredModelMBean对象的setModelMBeanInfo方法。
创建了ModelMBean对象后(RequiredModelMBean implements ModelMBean),需要调用ModelMBean接口的setManagedResource()方法将其与托管资源关联,该方法如下:
public void setManagedResource(Object managedResource, String managedResourceType) ;
managedResourceType的值可以为ObjectReference, Handle, IOR, EJBHandle或RMIReference,但当前只支持ObjectReference.
在注册时没有什么特别之处,只是需要注意下通过工具类获得MBean即可
package com.test.jmx.modelBean;
import com.sun.jdmk.comm.HtmlAdaptorServer;
import javax.management.*;
import javax.management.modelmbean.RequiredModelMBean;
import java.lang.management.ManagementFactory;
public class HelloAgent {
public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName helloName = new ObjectName("MyMBean:name=HelloWorld");
//Hello hello = new Hello();
RequiredModelMBean hello = ModelMBeanUtils.createModelerMBean();
server.registerMBean(hello, helloName);
ObjectName adapterName = new ObjectName("MyMBean:name=htmladapter,port=8082");
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
server.registerMBean(adapter, adapterName);
adapter.start();
}
}
Model MBean可以动态配置。试想一下这个应用场景:由于安全或其他原因,系统要把某个MBean公开的可管理方法隐藏起来。这时,如果你是用标准MBean,这需要修改接口类,然后重新编译发布;如果用 Apache commons-modeler(如果不想总要维护MBean这个借口,那么可以使用Apache的commons-modeler来辅助开发MBean,所有的MBean都装配在XML文件中)来写的模型MBean,则只需要修改XML文件就行了,不需要重新编译发布(可能要重启一下系统)。这就是Model Mbean 优势之所在了。
MXBeans
一个的MXBean是一种MBean的只引用一组预定义的数据类型。通过这种方式,您可以确保您的 MBean 可供任何客户端(包括远程客户端)使用,而无需客户端有权访问代表您的 MBean 类型的特定于模型的类。MXBean 提供了一种将相关值捆绑在一起的便捷方法,而无需专门配置客户端来处理这些捆绑包。
与Standard MBean 的方式相同,MXBean 是通过编写一个被调用的 Java 接口SomethingMXBean
和一个实现该接口的 Java 类来定义的。但是,与标准 MBean 不同,MXBean 不需要调用 Java 类Something
。接口中的每个方法都定义了 MXBean 中的属性或操作。注释@MXBean
也可用于注释 Java 接口,而不是要求接口名称后跟 MXBean 后缀。
package com.example;
public interface QueueSamplerMXBean {
public QueueSample getQueueSample();
public void clearQueue();
}
package com.example;
import java.util.Date;
import java.util.Queue;
public class QueueSampler implements QueueSamplerMXBean {
private Queue<String> queue;
public QueueSampler (Queue<String> queue) {
this.queue = queue;
}
public QueueSample getQueueSample() {
synchronized (queue) {
return new QueueSample(new Date(),
queue.size(), queue.peek());
}
}
public void clearQueue() {
synchronized (queue) {
queue.clear();
}
}
}
package com.example;
import java.beans.ConstructorProperties;
import java.util.Date;
public class QueueSample {
private final Date date;
private final int size;
private final String head;
@ConstructorProperties({"date", "size", "head"})
public QueueSample(Date date, int size,
String head) {
this.date = date;
this.size = size;
this.head = head;
}
public Date getDate() {
return date;
}
public int getSize() {
return size;
}
public String getHead() {
return head;
}
}
package com.example;
import java.lang.management.ManagementFactory;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import javax.management.MBeanServer;
import javax.management.ObjectName;
public class Main {
public static void main(String[] args) throws Exception {
MBeanServer mbs =
ManagementFactory.getPlatformMBeanServer();
ObjectName mxbeanName = new ObjectName("com.example:type=QueueSampler");
Queue<String> queue = new ArrayBlockingQueue<String>(10);
queue.add("Request-1");
queue.add("Request-2");
queue.add("Request-3");
QueueSampler mxbean = new QueueSampler(queue);
mbs.registerMBean(mxbean, mxbeanName);
System.out.println("Waiting...");
Thread.sleep(Long.MAX_VALUE);
}
}
如何定义MBean类
(1)直接实现MBean接口
(2)extend一个标准的MBean类:但是注意在子类中,只可以覆写父MBean的方法但是不可以添加新的方法,而且它必须自身提供一个public构造函数因为构造函数是不可以继承的。
(3)创建一个新的MBean接口让其extend一个老的MBean接口,并且让新的MBean类实现新的MBean接口
(4)创建一个新的MBean接口让其extend一个不是MBean接口的接口,并且让新的MBean类实现新的MBean接口。
MBean开发准则
(1)MBean必须是一个具体类(可以实例化的)
(2)MBean必须有public构造函数,可以有多个
(3)MBean必须实现自己的MBean接口(Standard MBean)或者DynamicBean接口 (Dynamic MBean)
此外,MBean可以可选的实现NotificationBroadcaster接口,如果这样做的话这个MBean就可以发送通知给有兴趣的监听器了。
JMX基本概念
受管资源(Manageable Resource)
受管资源是任何应用,设备,实体,只要可以被Java访问或者被封装为Java对象的
那么谁来管这些受管资源呢?->MBean
MBean(Managed Bean)
MBean是在JMX规范中,满足命名规则和继承规范的Java类,它们可以用来操作和访问受管资源(通过一些属性和方法).MBean有3类(Standard,Dynamic,Model)
那么谁来管这些MBean呢?或者MBean生存在哪里呢?-> MBeanServer
MBean服务器(MBean Server)
MBeanServer是JMX规范中,用于管理一组MBean的Java类。所有的MBean被一视同仁的看待。MBeanServer向MBean暴露了一组用于管理MBean的接口,并且可以用于查询MBean
MBean要起作用,必须有一些底层服务支撑他们,这些服务就由JMXAgent提供
JMX代理(JMX Agent)
JMXAgent是一个Java进程,用于提供一组用来管理MBean组的服务。它是MBean服务器的容器。
那么,谁来和使用JMXAgent,谁来和JMXAgent交互呢?->协议适配器和连接器
协议适配器和连接器(Protocol Adaptors and Connectors)
这些适配器和连接器是用于暴露JMXAgent给形形×××的协议或者客户端使用的中间桥梁。适配器(只存在在JMXAgent中),连接器(JMXAgent和客户端各有一个对象)
管理应用程序 (Management Application)
Management Application是对访问,配置,操作受管资源有兴趣的应用程序。典型的,比如JBoss中的jmx-console.
通知(Notification)
通知是MBean或者MBeanServer发出的用于包含事件,断言或者通用信息的Java对象。MBean或者Java对象可以注册Listener来接收这些通知。
设备(Instrumentation)
设备是使用MBean或者一组MBean来暴露受管资源的进程
JMX的架构
JMX采用的是3层的架构模型:
分配层:(Distributed Layer)
这一层主要包含了能JMXAgent和外部世界进行通信的组件。有两种类型的交互,一种称为adaptor,一种称为connector.
代理层: (Agent Layer)
这一层主要包含了Agent和MBeanServer,前者为了方便管理MBean,提供了4类服务(timer,monitoring,dynamic MBean loading,relationship service)。后者用于注册MBean,并且是JMX架构的核心组件。
设备层:(Instrumentation Layer)
这一层主要包含了MBean,每个MBean可通过API来使用,或者,操作一个受管资源
Notification
一个MBean提供的管理接口允许代理对其管理资源进行控制和配置。然而,对管理复杂的分布式系统来说,这些接口知识提供了一部分功能。通常,管理应用程序需要对状态变化或者当特别情况发生变化时作出反映。Notification起到了MBean之间的沟通桥梁的作用。JMX Notification模型和Java Event模型类似,将一些重要的信息,状态的转变,数据的变更传递给Notification Listener,以便资源的管理。
通知模型仅仅涉及了在同一个JMX代理中的管理构件之间的事件传播。JMX通知模型依靠以下几个部分:
- Notification,一个通用的事件类型,该类标识事件的类型,可以被直接使用,也可以根据传递的事件的需要而被扩展。
- NotificationListener接口,接受通知的对象需实现此接口。
- NotificationFilter接口,作为通知过滤器的对象需实现此接口,为通知监听者提供了一个过滤通知的过滤器。
- NotificationBroadcaster接口,通知发送者需实现此接口,该接口允许希望得到通知的监听者注册。
发送一个通用类型的通知,任何一个监听者都会得到该通知。因此,监听者需提供过滤器来选择所需要接受的通知。任何类型的MBean,标准的或动态的,都可以作为一个通知发送者,也可以作为一个通知监听者,或两者都是。
package com.test.jmx.notification;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
public class XiaoSi extends NotificationBroadcasterSupport implements XiaoSiMBean {
private int seq = 0;
/*
* 必需继承NotificationBroadcasterSupport
* 此类只有一个hi方法,方法只有两句:创建一个Notification消息包,然后将包发出去
* 如果你还要在消息包上附加其他数据,Notification还有一个setUserData方法可供使用
*/
@Override
public void hi() {
Notification n = new Notification(//创建一个信息包
"xiaosi.hi",//给这个Notification起个名称
this,//由谁发出的Notification
++seq,//一系列通知中的序列号,可以设置任意数值
System.currentTimeMillis(),//发出时间
"Xiaosi"//发出信息的消息文本
);
sendNotification(n);
}
}
package com.test.jmx.notification;
public interface XiaoSiMBean {
public void hi();
}
接下去创建Hello类的Listener(HelloListener.java),用于监听Notification的消息包并处理。
package com.test.jmx.notification;
import com.test.jmx.Hello;
import javax.management.Notification;
import javax.management.NotificationListener;
public class HelloListener implements NotificationListener {
@Override
public void handleNotification(Notification notification, Object handback) {
System.out.println("----------HelloListener-Begin------------");
System.out.println("\ttype = "+ notification.getType());
System.out.println("\tsource = "+notification.getSource());
System.out.println("\tseq = "+notification.getSequenceNumber());
System.out.println("\tsend time = "+notification.getTimeStamp());
System.out.println("\tmessage="+notification.getMessage());
System.out.println("----------HelloListener-End------------");
if (handback != null) {
if (handback instanceof Hello) {
Hello hello = (Hello)handback;
hello.printHello(notification.getMessage());
}
}
}
}
package com.test.jmx.notification;
import com.sun.jdmk.comm.HtmlAdaptorServer;
import com.test.jmx.Hello;
import javax.management.*;
import java.lang.management.ManagementFactory;
public class HelloAgent {
public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException,
InstanceAlreadyExistsException, MBeanRegistrationException {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName helloName = new ObjectName("MyMBean:name=HelloWorld");
Hello hello = new Hello();
server.registerMBean(hello,helloName);
ObjectName adapterName = new ObjectName("MyBean:name=htmladapter,port=8082");
HtmlAdaptorServer adapter = new HtmlAdaptorServer();
server.registerMBean(adapter,adapterName);
XiaoSi xs = new XiaoSi();
server.registerMBean(xs,new ObjectName("MyMBean:name=xiaosi"));
xs.addNotificationListener(new HelloListener(),null,hello);
adapter.start();
}
}
辅助元数据类
辅助元数据类用来描述管理构件。辅助元数据类不仅被用来内省标准管理构件,也被动态管理构件用来进行自我描述。这些类根据属性、操作、构建器和通告描述了管理接口。JMX代理通过这些元数据类管理所有管理构件,而不管这些管理构件的类型。部分辅助元类如下:
- MBeanInfo–包含了属性、操作、构建器和通知的信息。
- MBeanFeatureInfo–为下面类的超类。
- MBeanAttributeInfo–用来描述管理构件中的属性。
- MBeanConstructorInfo–用来描述管理构件中的构建器。
- MBeanOperationInfo–用来描述管理构件中的操作。
- MBeanParameterInfo–用来描述管理构件操作或构建器的参数。
- MBeanNotificationInfo–用来描述管理构件发出的通知。