标签:
JMX是一种JAVA的正式规范,它主要目的是让程序有被管理的功能。试想你开发了一个软件(如WEB网站),它是在24小时不间断运行的,那么你可能会想要“监控”这个软件的运行情况,比如收到了多少数据,有多少人登录等等。或者你又想“配置”这个软件,比如现在访问人数比较多,你想把数据连接池设置得大一些。
JMX 全称是 Java Management Extensions, Java5.0开始引入,提供连接、监控和管理远程JVM的方式。
MBean
一个MBean是一个被管理的Java对象,有点类似于JavaBean,一个设备、一个应用或者任何资源都可以被表示为MBean,MBean会暴露一个接口对外,这个接口可以读取或者写入一些对象中的属性,通常一个MBean需要定义一个接口,以MBean结尾,例如:DataSourceMBean, 格式为XXXMBean,这个是规范,必须得遵守。
MBeanServer
MBean的容器,提供远程访问、命名空间管理和安全服务。
JMX 指定了在 MBeanServer 和 JMX 客户之间通信所使用的协议,协议可以在各种传输机制上运行。可以使用针对本地连接的内置传输,及通过 RMI、socket 或 SSL 的远程传输(可以通过 JMX Connector API 创建新的传输)。
示例:
public interface HelloMBean { public String getName(); public void setName(String name); public void printHello(); public void printHello(String whoName); }
public class Hello implements HelloMBean { private String name = "loull"; @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name; } @Override public void printHello() { System.out.println("Hello, " + name); } @Override public void printHello(String whoName) { System.out.println("Hello, " + whoName); } }
本地agent
import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; public class HelloAgent { private static final String MBEAN_NAME = "com.jmxtest:type=HelloMBean"; public static void main(String[] args) throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, InterruptedException { // MBeanServer server = MBeanServerFactory.createMBeanServer(); MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName helloName = new ObjectName(MBEAN_NAME); Hello hello = new Hello(); server.registerMBean(hello, helloName); System.out.println("start..."); Thread.currentThread().join(); } }
这样就可以了。然后我们就可以使用jconsole进行监控和管理了。需要注意的是这里需要使用getPlatformMBeanServer才能够被jconsole监控到,创建新的MBeanServer是监控不到的。原因是JConsole通过Attach API动态attach到已经运行的目标JVM,然后命令其动态的load了JDK_HOME/lib/management-agent.jar这个agent包,这个agent包的agentmain方法会对PlatformMBeanServer进行RMI注册和监听。
html agent
import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import com.sun.jdmk.comm.HtmlAdaptorServer; public class HelloHtmlAgent { private static final String MBEAN_NAME = "com.alipay.jmxtest:type=HelloMBean"; public static void main(String[] args) throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { //创建MBeanServer MBeanServer server = MBeanServerFactory.createMBeanServer(); //创建MBean ObjectName ObjectName helloName = new ObjectName("MBeanTest:name=HelloWorld"); //注册MBean Hello hello = new Hello(); 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....."); } }
运行HelloAgent,然后打开网页:http://localhost:8082/。单击“name=HelloWorld”链接进入。
说明:
先创建了一个MBeanServer,用来做MBean的容器 将Hello这个类注入到MBeanServer中,注入需要创建一个ObjectName类 创建一个AdaptorServer,这个类将决定MBean的管理界面,这里用最普通的Html型界面。AdaptorServer其实也是一个MBean。 "MBeanTest:name=HelloWorld"的名字是有一定规则的,格式:“域名:name=MBean名称”,域名和MBean名称都可以任意取。
在实际系统中我们可以把name变成决定数库链接池的变量,这样我就可以对系统的运行参数进行实现的监控和配置(管理)。
检测虚拟机当前的状态总是 Java 开放人员所关心的,从 Java SE 5 之后,java.lang.management
包里面包括了许多MXBean的接口类和 LockInfo、MemoryUsage、MonitorInfo 和 ThreadInfo 等类。从名字可以看出,该包提供了虚拟机内存分配、垃圾收集(GC)情况、操作系统层、线程调度和共享锁,甚至编译情况的检测机制。
JVM在启动的时候会默认将这些内建的MBean注册到PlatfromMBeanServer。这样一来,Java 的开发人员就可以很简单地为自己做一些轻量级的系统检测,来确定当前程序的各种状态,以便随时调整。jconsole的MBeans中就能够看到这些系统MXBeans。
具体有如下这些MXBean Package java.lang.management:
如果我们要想获得这些信息,只需要简单的通过java.lang.management.ManagementFactory
这个工厂类(单例)来获得相应的的MXBean,然后就可以通过这个MBean获取其监控的数据了:
RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean(); // Get the standard attribute "VmVendor" String vendor = mxbean.getVmVendor(); // Or by calling the getPlatformMXBean or getPlatformMXBeans method: RuntimeMXBean mxbean = ManagementFactory.getPlatformMXBean(RuntimeMXBean.class); // Get the standard attribute "VmVendor" String vendor = mxbean.getVmVendor();
或者Construct an MXBean proxy instance that forwards the method calls to a given MBeanServer:
MBeanServerConnection mbs; // Connect to a running JVM (or itself) and get MBeanServerConnection // that has the JVM MBeans registered in it ... // Get a MBean proxy for RuntimeMXBean interface RuntimeMXBean proxy = ManagementFactory.getPlatformMXBean(mbs, RuntimeMXBean.class); // Get standard attribute "VmVendor" String vendor = proxy.getVmVendor();
Tomcat有个Tomcat Manager工程就是通过JMXProxyServlet暴露JMX监控项的。它的实现其实非常简单,就是根据用户指定的MBeanName和Attribute,在platformMBeanServer中访问该MBean的属性而已。
核心代码就是这几个函数:
public void getAttribute(PrintWriter writer, String onameStr, String att) { try { ObjectName oname = new ObjectName(onameStr); Object value = mBeanServer.getAttribute(oname, att); writer.println("OK - Attribute get ‘" + onameStr + "‘ - " + att + "= " + escape(value.toString())); } catch (Exception ex) { writer.println("Error - " + ex.toString()); } } public void setAttribute( PrintWriter writer, String onameStr, String att, String val ) { try { ObjectName oname=new ObjectName( onameStr ); String type=registry.getType(oname, att); Object valueObj=registry.convertValue(type, val ); mBeanServer.setAttribute( oname, new Attribute(att, valueObj)); writer.println("OK - Attribute set"); } catch( Exception ex ) { writer.println("Error - " + ex.toString()); } } public void listBeans( PrintWriter writer, String qry ) { Set names = null; try { names=mBeanServer.queryNames(new ObjectName(qry), null); writer.println("OK - Number of results: " + names.size()); writer.println(); } catch (Exception e) { writer.println("Error - " + e.toString()); return; } Iterator it=names.iterator(); while( it.hasNext()) { ObjectName oname=(ObjectName)it.next(); writer.println( "Name: " + oname.toString()); try { MBeanInfo minfo=mBeanServer.getMBeanInfo(oname); // can‘t be null - I thinl String code=minfo.getClassName(); if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) { code=(String)mBeanServer.getAttribute(oname, "modelerType"); } writer.println("modelerType: " + code); MBeanAttributeInfo attrs[]=minfo.getAttributes(); Object value=null; for( int i=0; i< attrs.length; i++ ) { if( ! attrs[i].isReadable() ) continue; if( ! isSupported( attrs[i].getType() )) continue; String attName=attrs[i].getName(); if( attName.indexOf( "=") >=0 || attName.indexOf( ":") >=0 || attName.indexOf( " ") >=0 ) { continue; } try { value=mBeanServer.getAttribute(oname, attName); } catch( Throwable t) { log("Error getting attribute " + oname + " " + attName + " " + t.toString()); continue; } if( value==null ) continue; if( "modelerType".equals( attName)) continue; String valueString=value.toString(); writer.println( attName + ": " + escape(valueString)); } } catch (Exception e) { // Ignore } writer.println(); } }
JMXProxyServlet的作用是将MBean数据以HTTP方式而不是RMI方式暴露出去。对于页面展示也好,或者穿越防火墙来说都是比较友好的。
标签:
原文地址:http://www.cnblogs.com/549294286/p/5189440.html