jmx介绍及示例

最近项目中要用到JMX,好好研究了一些,发现网上也有很多没有解释清楚的,自己动手试过后总结一下。
一、什么是jmx
JMX (Java Management Extensions)是一个为应用程序,设备,系统等植入管理功能的框架。JMX是一个规范,jdk5之前有很多实现,JDK5.0则内嵌了进来,安装JDK5.0就可以开发基于JMX的代码了。简而言之,使用jmx我们可以监控jvm的运行情况,并且可扩展我们自己想要添加的监控逻辑。
二、jmx架构
JMX架构

  1. Probe Level(设备层)负责资源的检测(获取信息),包含MBeans,通常也叫做Instrumentation Level。
  2. The Agent Level 或者叫做MBean Server,是JMX的核心,连接这个Mbeans和应用程序。
  3. Remote Management Level通过connectors和adaptors来远程操作MBeanServer, Applications可以是大家比较熟悉的控制台,例如JConsole。
    三、MBean(Managed beans)
    一个MBean是一个被管理的java对象,有点类似于Javabean,一个设备、一个应用或者任何资源都可以被表示为MBean。MBean会暴露一个对外接口,通过这个接口可以读取或者写入一些对象中的属性,操作对象的方法。JMX提供了四种MBean,分别是标准MBean、动态MBean、模型MBean和开发MBean。
    (1)标准MBean
    Standard Mbean是最简单的MBean,他能管理的资源必须定义在接口中,然后MBean必须实现这个接口,命名必须遵守一定的规范。
    示例:
    1、编写MBean的接口:ControllerMBean.java
public interface ControllerMBean {
    /*需遵从的规范:
    1、名字必须以MBean结尾
    2、必须有相关属性的getXXX()和setXXX()方法名*/

    public void setName(String name);
    public String getName();
    public String status();  
    public void start();  
    public void stop();  
}

2、编写MBean接口的实现类:Controller.java

public class Controller implements ControllerMBean {
    /*需遵从的规范:
    1、名字必须和接口中 "MBean" 的前缀相同(不知道是不是新版本的规定,很多都没有说明,期初没有相同调试程序一直不过)
    2、实现接口中的方法*/
    private String name;

    @Override
    public void setName(String name) {
        // TODO Auto-generated method stub
        this.name=name;
    }

    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return this.name;
    }

    @Override
    public String status() {
        // TODO Auto-generated method stub
        System.out.println("this is a Controller MBean,name is " + this.name);
        return "this is a Controller MBean,name is " + this.name;   }

    @Override
    public void start() {
        // TODO Auto-generated method stub

    }

    @Override
    public void stop() {
        // TODO Auto-generated method stub

    }

3、创建一个Agent类:ControllerTestJMX.java

public class ControllerTestJMX {
    public static void main(String[] args) throws MalformedObjectNameException, NullPointerException, InstanceAlreadyExistsException, NotCompliantMBeanException, InstanceNotFoundException, ReflectionException, MBeanException
    {
//      MBeanServer mbs = MBeanServerFactory.createMBeanServer();//不能在jconsole中使用  
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();//可在jconsole中使用 

        //创建标准MBean
        Controller controller = new Controller();
        ObjectName controllerName = new ObjectName("myBean:name=controller,type=test");
        //将标准MBean注册到MBeanServer中
        mbs.registerMBean(controller, controllerName);
        mbs.invoke(controllerName, "status", null, null);
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

添加后可以打开JConsole查看:

这里写图片描述

(2)动态MBean
动态MBean的最大的特点是可以在运行期暴露自己的管理接口,因此具有更好的灵活性。它的实现是通过实现一个特定的接口DynamicMBean。
当DynamicMBean在JMX agent中注册后,DynamicMBean其实就和StandardMBean 是一样的,外界将首先通过getMBeanInfo获得一个管理接口,其中包含attributes 和 operations 名称,然后才将调用DynamicMBean的getters, setters 和invoke方法。 适合管理那些已经存在的资源和代码。
1、 TestDynamicMBean.java:实现了DynamicMBean接口

public class TestDynamicMBean implements DynamicMBean {
    private String mc = "myDynamicMBean";
    private MBeanAttributeInfo[] dAttributes;
    private MBeanOperationInfo[] opers=new MBeanOperationInfo[3];
    private MBeanConstructorInfo[] constructors = new MBeanConstructorInfo[1];  
    MBeanInfo info;

    private String value="hello";
    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getMc() {
        return mc;
    }

    public void setMc(String name) {
        this.mc = name;
    }

    public void printName()
    {
        System.out.println(mc);
    }

    public TestDynamicMBean()
    {
        init();
        System.out.println("构造函数");
    }
     public void init()
     {
         Constructor[] thisconstructors = this.getClass().getConstructors();  
            constructors[0] = new MBeanConstructorInfo("TestDynamicMBean(): Constructs a TestDynamicMBean object", thisconstructors[0]);
        //可用的方法是在 MBeanInfo 接口中指定的
            dAttributes =  new   MBeanAttributeInfo[] {
                    new   MBeanAttributeInfo( "mc" ,  "String" ,  "缓存名称" ,  true ,   true ,  false )};
           /* MBeanOperationInfo opers[] = {
                    new   MBeanOperationInfo( "printName" , "print" , null , "void" ,MBeanOperationInfo.ACTION),
                    new   MBeanOperationInfo( "setMc" , "setMc" , null , "void" ,MBeanOperationInfo.ACTION),
                    new   MBeanOperationInfo( "addAttribute" , "addAttribute" , null , "void" ,MBeanOperationInfo.ACTION)

            };*/
            opers[0]= new   MBeanOperationInfo( "printName" , "print" , null , "void" ,MBeanOperationInfo.ACTION);
            opers[1]= new   MBeanOperationInfo( "setMc" , "setMc" , null , "void" ,MBeanOperationInfo.ACTION);
            opers[2]= new   MBeanOperationInfo( "addAttribute" , "addAttribute" , null , "void" ,MBeanOperationInfo.ACTION);

            info =  new   MBeanInfo( this .getClass().getName(),
                                            "TestDynamic" ,
                                            dAttributes,
                                            null ,
                                            opers,
                                            null );
     }
     /*动态添加属性
      *
      */

    public void addAttribute()
    {
        System.out.println("开始调用addAttribute");

        dAttributes=new MBeanAttributeInfo[2];      
        dAttributes[0]=new   MBeanAttributeInfo( "name" ,  "String" ,  "缓存name" ,  true ,   true ,  false );
        dAttributes[1]=new   MBeanAttributeInfo( "value" ,  "String" ,  "缓存value" ,  true ,   true ,  false );
        System.out.println("已增加value属性");
        info =  new   MBeanInfo( this .getClass().getName(),
                "TestDynamic" ,
                dAttributes,
                null ,
                opers,
                null );
}

    @Override
    public Object getAttribute(String arg0) throws AttributeNotFoundException,
            MBeanException, ReflectionException {
        // TODO Auto-generated method stub
        System.out.println("getAttribute");

         if (arg0== null ){
             throw   new   AttributeNotFoundException();
         }
         if ( "name" .equalsIgnoreCase(arg0)){
             return   getMc();
         }
         if ( "value" .equalsIgnoreCase(arg0)){
             return   getValue();
         }
         throw   new   AttributeNotFoundException(); 
    }

    @Override
    public AttributeList getAttributes(String[] arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public MBeanInfo getMBeanInfo() {
        // TODO Auto-generated method stub
        System.out.println("调用getMBeanInfo方法");
        System.out.println(dAttributes.length);
        return   info;
    } 


    @Override
    public Object invoke(String arg0, Object[] arg1, String[] arg2)
            throws MBeanException, ReflectionException {
        // TODO Auto-generated method stub
        if ( "printName" .equals(arg0)){
            printName();
        }
        if ( "addAttribute" .equals(arg0)){
            addAttribute();
        }
        return   null ; 
    }

    @Override
    public void setAttribute(Attribute arg0) throws AttributeNotFoundException,
            InvalidAttributeValueException, MBeanException, ReflectionException {
        // TODO Auto-generated method stub
        System.out.println("setAttribute");
        String name = arg0.getName();
        Object value = arg0.getValue();
        if ( "name" .equalsIgnoreCase(name)){
            this .setMc(String.valueOf(value));
            return ;
        }
        if ( "value" .equalsIgnoreCase(name)){
            this .setValue(String.valueOf(value));
            return ;
        }
        throw   new   AttributeNotFoundException(); 
    }

    @Override
    public AttributeList setAttributes(AttributeList arg0) {
        // TODO Auto-generated method stub
        return null;
    }

2、Agent类:ControllerTestJMX.java

public class ControllerTestJMX {
    public static void main(String[] args) throws MalformedObjectNameException, NullPointerException, InstanceAlreadyExistsException, NotCompliantMBeanException, InstanceNotFoundException, ReflectionException, MBeanException
    {
//      MBeanServer mbs = MBeanServerFactory.createMBeanServer();//不能在jconsole中使用  
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();//可在jconsole中使用 

        //创建动态MBean
        TestDynamicMBean dynamicMBean = new TestDynamicMBean();
        ObjectName dynamicMBeanName = new ObjectName("myDynamicMBean:name=TestDynamicMBean,type=testDynamic");

      //将动态MBean注册到MBeanServer中
        mbs.registerMBean(dynamicMBean, dynamicMBeanName);
       // mbs.invoke(dynamicMBeanName, "status", null, null);

        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

打开jconsole后进入MBean中会看到刚才注册的myDynamicMBean,点开节点,到操作,点击addAttribute方法(添加属性),提示成功添加属性。再次新打开一个jconsole窗口,可看到新添加的属性value。
这里写图片描述
四、监控
上面我们已经学习到如何开发jmx用例,如何获得监控的数据是我们的重点,下面通过监控tomcat来描述,tomcat实现了jmx,并且已注册了多种MBean供开发人员监控其性能。
这里写图片描述
下面实现对tomcat的大部分参数的监控:
1、首先配置tomcat,在catalina.sh中配置JAVA_OPTS
JAVA_OPTS=”-Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=10.25.4.129
-Dcom.sun.management.jmxremote.port=8050 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false”
启动tomcat。其中8050就是jmx远程连接端口(可以修改为其它)

2、编写测试类

public class JmxTest {

    public static  void main(String[] args){
     try { 

         String jmxURL = "service:jmx:rmi:///jndi/rmi://10.25.4.129:8050/jmxrmi";//tomcat jmx url   
         JMXServiceURL serviceURL = new JMXServiceURL(jmxURL);

    /*下面这三行代码提供注册所需的用户名,密码,tomcat中参数-Dcom.sun.management.jmxremote.authenticate="false"  配为false,就不需要了*/ 
         Map map = new HashMap(); 
         String[] credentials = new String[] { "monitorRole", "QED" }; 
         map.put("jmx.remote.credentials", credentials); 

         JMXConnector connector = JMXConnectorFactory.connect(serviceURL, map); 
         MBeanServerConnection mbsc = connector.getMBeanServerConnection(); 

         //端口最好是动态取得   
         ObjectName threadObjName = new ObjectName("Catalina:name=\"http-bio-9666\",type=ThreadPool"); 
 /*其中Catalina:name="http-bio-9666",type=ThreadPool是tomcat中已有的MBean*/
         MBeanInfo mbInfo = mbsc.getMBeanInfo(threadObjName); 

         String attrName = "currentThreadCount";//tomcat的线程数对应的属性值   
         MBeanAttributeInfo[] mbAttributes = mbInfo.getAttributes(); 
         System.out.println("currentThreadCount:" + mbsc.getAttribute(threadObjName, attrName)); 

         //heap   
         for (int j = 0; j < mbsc.getDomains().length; j++) { 
             System.out.println("###########" + mbsc.getDomains()[j]); 
         } 
         Set MBeanset = mbsc.queryMBeans(null, null); 
         System.out.println("MBeanset.size() : " + MBeanset.size()); 
         Iterator MBeansetIterator = MBeanset.iterator(); 
         while (MBeansetIterator.hasNext()) { 
             ObjectInstance objectInstance = (ObjectInstance) MBeansetIterator.next(); 
             ObjectName objectName = objectInstance.getObjectName(); 
             String canonicalName = objectName.getCanonicalName(); 
             System.out.println("canonicalName : " + canonicalName); 
             if (canonicalName.equals("Catalina:host=localhost,type=Cluster")) { 
                 // Get details of cluster MBeans   
                 System.out.println("Cluster MBeans Details:"); 
                 System.out.println("========================================="); 
                 //getMBeansDetails(canonicalName);   
                 String canonicalKeyPropList = objectName.getCanonicalKeyPropertyListString(); 
             } 
         } 
         //------------------------- system ----------------------   
         ObjectName runtimeObjName = new ObjectName("java.lang:type=Runtime"); 
         System.out.println("厂商:" + (String) mbsc.getAttribute(runtimeObjName, "VmVendor")); 
         System.out.println("程序:" + (String) mbsc.getAttribute(runtimeObjName, "VmName")); 
         System.out.println("版本:" + (String) mbsc.getAttribute(runtimeObjName, "VmVersion")); 
         Date starttime = new Date((Long) mbsc.getAttribute(runtimeObjName, "StartTime")); 
         SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
         System.out.println("启动时间:" + df.format(starttime)); 

         Long timespan = (Long) mbsc.getAttribute(runtimeObjName, "Uptime"); 
         System.out.println("连续工作时间:" + JmxTest.formatTimeSpan(timespan)); 
         //------------------------ JVM -------------------------   
         //堆使用率   
         ObjectName heapObjName = new ObjectName("java.lang:type=Memory"); 
         MemoryUsage heapMemoryUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(heapObjName, 
                 "HeapMemoryUsage")); 
         long maxMemory = heapMemoryUsage.getMax();//堆最大   
         long commitMemory = heapMemoryUsage.getCommitted();//堆当前分配   
         long usedMemory = heapMemoryUsage.getUsed(); 
         System.out.println("heap:" + (double) usedMemory * 100 / commitMemory + "%");//堆使用率   

         MemoryUsage nonheapMemoryUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(heapObjName, 
                 "NonHeapMemoryUsage")); 
         long noncommitMemory = nonheapMemoryUsage.getCommitted(); 
         long nonusedMemory = heapMemoryUsage.getUsed(); 
         System.out.println("nonheap:" + (double) nonusedMemory * 100 / noncommitMemory + "%"); 

         ObjectName permObjName = new ObjectName("java.lang:type=MemoryPool,name=Perm Gen"); 
         MemoryUsage permGenUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(permObjName, "Usage")); 
         long committed = permGenUsage.getCommitted();//持久堆大小   
         long used = heapMemoryUsage.getUsed();//   
         System.out.println("perm gen:" + (double) used * 100 / committed + "%");//持久堆使用率   

         //-------------------- Session ---------------    
         ObjectName managerObjName = new ObjectName("Catalina:type=Manager,*"); 
         Set<ObjectName> s = mbsc.queryNames(managerObjName, null); 
         for (ObjectName obj : s) { 
             System.out.println("应用名:" + obj.getKeyProperty("path")); 
             ObjectName objname = new ObjectName(obj.getCanonicalName()); 
             System.out.println("最大会话数:" + mbsc.getAttribute(objname, "maxActiveSessions")); 
             System.out.println("会话数:" + mbsc.getAttribute(objname, "activeSessions")); 
             System.out.println("活动会话数:" + mbsc.getAttribute(objname, "sessionCounter")); 
         } 

         //----------------- Thread Pool ----------------   
         ObjectName threadpoolObjName = new ObjectName("Catalina:type=ThreadPool,*"); 
         Set<ObjectName> s2 = mbsc.queryNames(threadpoolObjName, null); 
         for (ObjectName obj : s2) { 
             System.out.println("端口名:" + obj.getKeyProperty("name")); 
             ObjectName objname = new ObjectName(obj.getCanonicalName()); 
             System.out.println("最大线程数:" + mbsc.getAttribute(objname, "maxThreads")); 
             System.out.println("当前线程数:" + mbsc.getAttribute(objname, "currentThreadCount")); 
             System.out.println("繁忙线程数:" + mbsc.getAttribute(objname, "currentThreadsBusy")); 
         } 

     } catch (Exception e) { 
         e.printStackTrace(); 
     } 
 } 

 public static String formatTimeSpan(long span) { 
     long minseconds = span % 1000; 

     span = span / 1000; 
     long seconds = span % 60; 

     span = span / 60; 
     long mins = span % 60; 

     span = span / 60; 
     long hours = span % 24; 

     span = span / 24; 
     long days = span; 
     return (new Formatter()).format("%1$d天 %2$02d:%3$02d:%4$02d.%5$03d", days, hours, mins, seconds, minseconds) 
             .toString(); 
 } 
}

关于ObjectName 可以正则匹配,这个一直没有实现,希望有成功经验的可以不吝赐教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值