1. MBean介绍
从上一篇Blog的内容可以看到,我们大多数代码工作量都是自定义的一个JavaBean,这个JavaBean有属性、访问器(get)、写操作(set),之后还可以定义一些非常规方法,比如执行核心管理操作的方法,甚至还可以深度调用full gc进行全面的资源回收。也就是说这个JavaBean并不像它的名字那么简单,它负责了一些监管任务等等。使用者灵活地使用JMX客户端工具,比如咱们上一章介绍的JConsole。借助Sprng框架,给开发者的感觉就是仅仅开发这种JavaBean就可以了。这种看似简单的JavaBean叫做MBean(管理构建),比较权威的定义是:在JMX规范中,管理构件定义如下:它是一个能代表管理资源的Java对象,遵从一定的设计模式,还需实现该规范定义的特定的接口。该定义了保证了所有的管理构件以一种标准的方式来表示被管理资源。
资源是什么?程序、日志、持久化数据、硬件、内存、网络吞吐量等等皆可看作是系统资源。管理资源暴露出一些信息,让外部能够进行远程查看和远程监管,这就是管理构建要做的事情。当然了管理构建MBean还得支持类似于JMS那种订阅/通知的机制。
2. MBean的类型
标准管理构建,学名——Standard MBean,本身暴露MBean需要符合某些命名规范,比如类结尾以MBean等等,但是使用了框架,这些限制就被透明化了。换句话说开发者只需要定制一个POJO即可,之后暴露即可。这也是上一章节的那些JMX——MBean。
public class ServerImpl { public final long startTime; public ServerImpl() { startTime = System.currentTimeMillis(); } }
之后对其进行监管
public class ServerMonitor implements ServerMonitorMBean { private final ServerImpl target; public ServerMonitor(ServerImpl target){ this.target = target; } public long getUpTime(){ return System.currentTimeMillis() - target.startTime; } }
之后就是暴露MBean服务
import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.ObjectName; public class Main { private static ObjectName objectName ; private static MBeanServer mBeanServer; public static void main(String[] args) throws Exception{ init(); manage(); } private static void init() throws Exception{ ServerImpl serverImpl = new ServerImpl(); ServerMonitor serverMonitor = new ServerMonitor(serverImpl); mBeanServer = MBeanServerFactory.createMBeanServer(); objectName = new ObjectName("objectName:id=ServerMonitor1"); mBeanServer.registerMBean(serverMonitor,objectName); } private static void manage() throws Exception{ Long upTime = (Long) mBeanServer.getAttribute(objectName, "upTime"); System.out.println(upTime); } }
动态代理构建,学名——DynamicMBean,这个笔者觉得有点像Java的反射机制的一个使用场景,就是在运行期间,你不知道你系统要调用哪个类、哪个方法,但是又想在运行期间顺利的调用到该方法,那怎么办?动态MBean可以解决这个问题。
实现javax.management.DynamicMBean 接口和它定义的方法,即可,该接口定义了以下6个方法:
getMBeanInfo
getAttribute
setAttribute
getAttributes
setAttributes
invoke
从方法名字上可以知晓和Java的反射机制差不多啊,都是获得对象的一些元数据,之后进行动态invoke。这里插一句,既然可以动态invoke调用动态MBean的方法,是不是就意味着可以像AOP面向切面编程一样对动态MBean进行横向侵入呢?答案是肯定的。由于DynamicMBean的接口是不变的,因此可以屏蔽实现细节。由于这种在运行期获取管理接口的特性,动态管理构件提供了更大的灵活性。
我们依然看看网上的一个经典实例import javax.management.*; import java.lang.reflect.*; public class ServerMonitor implements DynamicMBean { private final ServerImpl target; private MBeanInfo mBeanInfo; public ServerMonitor(ServerImpl target){ this.target = target; } // 实现获取被管理的 ServerImpl 的 upTime public long upTime(){ return System.currentTimeMillis() - target.startTime; } //javax.management.MBeanServer 会通过查询 getAttribute("Uptime") 获得 "Uptime" 属性值 public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { if(attribute.equals("UpTime")){ return upTime(); } return null; } // 给出 ServerMonitor 的元信息。 public MBeanInfo getMBeanInfo() { if (mBeanInfo == null) { try { Class cls = this.getClass(); // 用反射获得 "upTime" 属性的读方法 Method readMethod = cls.getMethod("upTime", new Class[0]); // 用反射获得构造方法 Constructor constructor = cls.getConstructor(new Class[] {ServerImpl.class}); // 关于 "upTime" 属性的元信息 : 名称为 UpTime,只读属性 ( 没有写方法 )。 MBeanAttributeInfo upTimeMBeanAttributeInfo = new MBeanAttributeInfo( "UpTime", "The time span since server start", readMethod, null); // 关于构造函数的元信息 MBeanConstructorInfo mBeanConstructorInfo = new MBeanConstructorInfo( "Constructor for ServerMonitor", constructor); //ServerMonitor 的元信息,为了简单起见,在这个例子里, // 没有提供 invocation 以及 listener 方面的元信息 mBeanInfo = new MBeanInfo(cls.getName(), "Monitor that controls the server", new MBeanAttributeInfo[] { upTimeMBeanAttributeInfo }, new MBeanConstructorInfo[] { mBeanConstructorInfo }, null, null); } catch (Exception e) { throw new Error(e); } } return mBeanInfo; } public AttributeList getAttributes(String[] arg0) { return null; } public Object invoke(String arg0, Object[] arg1, String[] arg2) throws MBeanException, ReflectionException { return null; } public void setAttribute(Attribute arg0) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { return; } public AttributeList setAttributes(AttributeList arg0) { return null; } }
开放式管理构建,学名——Open MBean,开放管理构件是一种专门化的动态管理构件,其中所有的与该管理构件相关的参数、返回类型和属性都围绕一组预定义的数据类型(String、Integer、Float 等)来建立,并且通过一组特定的接口来进行自我描述。JMX代理通过获得一个OpenMBeanInfo对象来获取开放管理构件的管理接口,OpenMBeanInfo是MbeanInfo的子类。很可惜,这个没实例,也不经常使用。希望经常使用这个的童鞋将这块补上。
模型MBean,学名——ModelMBean,模型管理构件也是一种专门化的动态管理构件。普通的动态管理构建通常缺乏一些管理系统所需要的支持:比如持久化MBean的状态、日志记录、缓存等等。如果让用户去实现这些功能确实比较繁琐。为了减轻用户的负担,JMX 提供商都会提供不同的ModelBean实现。其中有一个接口是Java规范中规定所有厂商必须实现的:javax.management.modelmbean.RequiredModelBean。通过配置描述信息,我们可以定制这个ModelBean,指定哪些MBean状态需要记入日志、如何记录以及是否缓存某些属性、缓存多久等等。还是使用网上实例
public class Server { private long startTime; public Server() { } public int start(){ startTime = System.currentTimeMillis(); return 0; } public long getUpTime(){ return System.currentTimeMillis() - startTime; } }
之后也是暴露如下
import javax.management.*; import javax.management.modelmbean.*; public class Main { public static void main(String[] args) throws Exception{ MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer(); RequiredModelMBean serverMBean = (RequiredModelMBean) mBeanServer.instantiate( "javax.management.modelmbean.RequiredModelMBean"); ObjectName serverMBeanName = new ObjectName("server: id=Server"); serverMBean.setModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName)); Server server = new Server(); serverMBean.setManagedResource(server, "ObjectReference"); ObjectInstance registeredServerMBean = mBeanServer.registerMBean((Object) serverMBean, serverMBeanName); serverMBean.invoke("start",null, null); Thread.sleep(1000); System.out.println(serverMBean.getAttribute("upTime")); Thread.sleep(5000); System.out.println(serverMBean.getAttribute("upTime")); } private static ModelMBeanInfo getModelMBeanInfoForServer(ObjectName objectName) throws Exception{ ModelMBeanAttributeInfo[] serverAttributes = new ModelMBeanAttributeInfo[1]; Descriptor upTime = new DescriptorSupport( new String[] { "name=upTime", "descriptorType=attribute", "displayName=Server upTime", "getMethod=getUpTime", }); serverAttributes[0] = new ModelMBeanAttributeInfo( "upTime", "long", "Server upTime", true, false, false, upTime); ModelMBeanOperationInfo[] serverOperations = new ModelMBeanOperationInfo[2]; Descriptor getUpTimeDesc = new DescriptorSupport( new String[] { "name=getUpTime", "descriptorType=operation", "class=modelmbean.Server", "role=operation" }); MBeanParameterInfo[] getUpTimeParms = new MBeanParameterInfo[0]; serverOperations[0] = new ModelMBeanOperationInfo("getUpTime", "get the up time of the server", getUpTimeParms, "java.lang.Long", MBeanOperationInfo.ACTION, getUpTimeDesc); Descriptor startDesc = new DescriptorSupport( new String[] { "name=start", "descriptorType=operation", "class=modelmbean.Server", "role=operation" }); MBeanParameterInfo[] startParms = new MBeanParameterInfo[0]; serverOperations[1] = new ModelMBeanOperationInfo("start", "start(): start server", startParms, "java.lang.Integer", MBeanOperationInfo.ACTION, startDesc); ModelMBeanInfo serverMMBeanInfo = new ModelMBeanInfoSupport( "modelmbean.Server", "ModelMBean for managing an Server", serverAttributes, null, serverOperations, null); //Default strategy for the MBean. Descriptor serverDescription = new DescriptorSupport( new String[] { ("name=" + objectName), "descriptorType=mbean", ("displayName=Server"), "type=modelmbean.Server", "log=T", "logFile=serverMX.log", "currencyTimeLimit=10" }); serverMMBeanInfo.setMBeanDescriptor(serverDescription); return serverMMBeanInfo; }
通常开发JMX都有以下几个步骤:
创建一个MBServer:mBeanServe
获得管理资源用的MBean:serverBean
给这个MBean一个ObjectName:serverMBeanName
将serverBean以serverMBeanName注册到mBeanServer上去。
这篇总结大多数是查看的网络资料,学习交流而已,比较理论化,以下是查询的几篇资料链接。JMX工作原理图如下
参考资料:
http://www.ibm.com/developerworks/cn/java/j-lo-jse63/index.html
http://www.cnblogs.com/aurawing/articles/1887060.html
http://baike.baidu.com/view/866268.htm