JMX超详细解读

1、JMX的定义  

  JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户能够在任何Java应用程序中使用这些代理和服务实现管理。这是官方文档上的定义,我看过不少次也没法很好的理解。我我的的理解是JMX让程序有被管理的功能,例如你开发一个WEB网站,它是在24小时不间断运行,那么你确定会对网站进行监控,如天天的UV、PV是多少;又或者在业务高峰的期间,你想对接口进行限流,就必须去修改接口并发的配置值。html

  应用场景:中间件软件WebLogic的管理页面就是基于JMX开发的,而JBoss则整个系统都基于JMX构架。java

对于一些参数的修改,网上有一段描述仍是比较形象的:linux

一、程序初哥通常是写死在程序中,到要改变的时候就去修改代码,而后从新编译发布。缓存

二、程序熟手则配置在文件中(JAVA通常都是properties文件),到要改变的时候只要修改配置文件,但仍是必须重启系统,以便读取配置文件里最新的值。tomcat

三、程序好手则会写一段代码,把配置值缓存起来,系统在获取的时候,先看看配置文件有没有改动,若有改动则从新从配置里读取,不然从缓存里读取。服务器

四、程序高手则懂得物为我所用,用JMX把须要配置的属性集中在一个类中,而后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页,以方便咱们对参数值进行修改。架构

2、JMX架构图:

从图中咱们能够看到,JMX的结构一共分为三层:并发

一、基础层:主要是MBean,被管理的资源。框架

MBean分为以下四种,我接下来主要介绍standard MBeandom

类型 描述
standard MBean 这种类型的MBean最简单,它能管理的资源(包括属性,方法,时间)必须定义在接口中,而后MBean必须实现这个接口。它的命名也必须遵循必定的规范,例如咱们的MBean为Hello,则接口必须为HelloMBean。
dynamic MBean 必须实现javax.management.DynamicMBean接口,全部的属性,方法都在运行时定义
open MBean 此MBean的规范还不完善,正在改进中
model MBean 与标准和动态MBean相比,你能够不用写MBean类,只需使用javax.management.modelmbean.RequiredModelMBean便可。RequiredModelMBean实现了ModelMBean接口,而ModelMBean扩展了DynamicMBean接口,所以与DynamicMBean类似,Model MBean的管理资源也是在运行时定义的。与DynamicMBean不一样的是,DynamicMBean管理的资源通常定义在DynamicMBean中(运行时才决定管理那些资源),而model MBean管理的资源并不在MBean中,而是在外部(一般是一个类),只有在运行时,才经过set方法将其加入到model MBean中。后面的例子会有详细介绍

二、适配层:MBeanServer,主要是提供对资源的注册和管理。

三、接入层:提供远程访问的入口。

 

接下来我这里会用程序来介绍三种访问JMX的方式:

3、JDK的小工具Jconsole访问

 一、 首先定义一个MBean接口,接口的命名规范为以具体的实现类为前缀(这个规范很重要)

 1 package jmx;
 2 
 3 public interface HelloMBean
 4 {
 5      public String getName();
 6         
 7      public void setName(String name);
 8         
 9      public String getAge();
10 
11      public void setAge(String age);
12         
13      public void helloWorld();
14         
15      public void helloWorld(String str);
16         
17      public void getTelephone();
18 }

二、定义一个实现类,实现上面的接口:

 1 package jmx;
 2 
 3 /*
 4  * 该类名称必须与实现的接口的前缀保持一致(即MBean前面的名称
 5  */
 6 public class Hello implements HelloMBean
 7 {
 8     private String name;
 9         
10     private String age;
11 
12     public void getTelephone()
13     {
14         System.out.println("get Telephone");
15     }
16 
17     public void helloWorld()
18     {
19         System.out.println("hello world");
20     }
21 
22     public void helloWorld(String str)
23     {
24         System.out.println("helloWorld:" + str);
25     }
26 
27     public String getName()
28     {
29         System.out.println("get name 123");
30         return name;
31     }
32 
33     public void setName(String name)
34     {
35         System.out.println("set name 123");
36         this.name = name;
37     }
38 
39     public String getAge()
40     {
41         System.out.println("get age 123");
42         return age;
43     }
44 
45     public void setAge(String age)
46     {
47         System.out.println("set age 123");
48         this.age = age;
49     }      
53 }

三、定义agent层:

 1 package jmx;
 2 
 3 import java.lang.management.ManagementFactory;
 4 
 5 import javax.management.JMException;
 6 import javax.management.MBeanServer;
 7 import javax.management.ObjectName;
 8 
 9 public class HelloAgent
10 {
11     public static void main(String[] args) throws JMException, Exception
12     {
13          MBeanServer server = ManagementFactory.getPlatformMBeanServer();
14          ObjectName helloName = new ObjectName("jmxBean:name=hello");
15          //create mbean and register mbean
16          server.registerMBean(new Hello(), helloName);
17          Thread.sleep(60*60*1000);
18     }
19 }

一、其中第13行是经过工厂类获取MBeanServer,用来作MBean的容器 。

二、第14行中的ObjectName中的取名是有必定规范的,格式为:“域名:name=MBean名称”,其中域名和MBean的名称能够任意取。这样定义后,就能够惟一标识咱们定义的这个MBean的实现类了。

三、第16行是将Hello这个类注入到MBeanServer中,注入须要建立一个ObjectName类 

这样,一个简单的JMX的DEMO已经写完了,如今咱们经过JDK提供的Jconsole来进行操做。

一、首先在本身的本地路径下:C:\Program Files (x86)\Java\jdk1.6.0_43\bin找到jconsole.exe这个小工具,双击打开:

二、双击打开咱们的本地进程:HelloAgent:

3.在这个界面上,咱们能够给程序中HelloMBean的属性赋值,也能够调用其中的方法:

四、控制台打印以下:

4、经过JMX提供的工具页访问

这里,咱们复用上面的接口和实现类,只须要改动适配层,这里须要到导入外部jar包jdmk

package jmx;

import java.lang.management.ManagementFactory;

import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;

import com.sun.jdmk.comm.HtmlAdaptorServer;

public class HelloAgent
{
    public static void main(String[] args) throws JMException, Exception
    {
         MBeanServer server = ManagementFactory.getPlatformMBeanServer();
         ObjectName helloName = new ObjectName("jmxBean:name=hello");
         //create mbean and register mbean
         server.registerMBean(new Hello(), helloName);
         
         ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082");   
         HtmlAdaptorServer adapter = new HtmlAdaptorServer();   
         server.registerMBean(adapter, adapterName);  
         adapter.start();
    }
}

咱们访问地址:http://localhost:8082,点击name=hello:

一、在这里建立一个AdaptorServer,这个类将决定MBean的管理界面,这里用最普通的Html型界面。AdaptorServer其实也是一个MBean。 

二、咱们能够看到这个工具页,其实与咱们上一个案例中的Jconsole中的管理界面相似,均可以操做资源中的属性和方法。

5、经过客户端程序进行远程访问

 一、这里须要对agent进行修改,增长ip和porta绑定部分的逻辑

package jmxTest;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class HelloAgent
{
    public static void main(String[] args) throws JMException, NullPointerException
    {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName helloName = new ObjectName("jmxBean:name=hello");
        //create mbean and register mbean
        server.registerMBean(new Hello(), helloName);
        try
        {
            //这个步骤很重要,注册一个端口,绑定url后用于客户端经过rmi方式链接JMXConnectorServer
            LocateRegistry.createRegistry(9999);
            //URL路径的结尾能够随意指定,但若是须要用Jconsole来进行链接,则必须使用jmxrmi
            JMXServiceURL url = new JMXServiceURL
                  ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
            JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
            System.out.println("begin rmi start");
            jcs.start();
            System.out.println("rmi start");
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
      
}
}

  写到这里,若是没有client进行远程链接,可使用Jconsole进行远程访问:

二、客户端Client程序,用于与agent进行远程链接:

 1 package jmx;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.management.Attribute;
 6 import javax.management.MBeanServerConnection;
 7 import javax.management.MBeanServerInvocationHandler;
 8 import javax.management.ObjectName;
 9 import javax.management.remote.JMXConnector;
10 import javax.management.remote.JMXConnectorFactory;
11 import javax.management.remote.JMXServiceURL;
12 
13 
14 public class Client
15 {
16     public static void main(String[] args) throws IOException, Exception, NullPointerException
17     {
18         JMXServiceURL url = new JMXServiceURL
19             ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
20         JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
21         
22         MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
23         //ObjectName的名称与前面注册时候的保持一致
24         ObjectName mbeanName = new ObjectName("jmxBean:name=hello");
25         
26         System.out.println("Domains ......");
27         String[] domains = mbsc.getDomains();
28         
29         for(int i=0;i<domains.length;i++)
30         {
31             System.out.println("doumain[" + i + "]=" + domains[i] );
32         }
33         
34         System.out.println("MBean count = " + mbsc.getMBeanCount());
35         //设置指定Mbean的特定属性值
36         //这里的setAttribute、getAttribute操做只能针对bean的属性
37         //例如对getName或者setName进行操做,只能使用Name,须要去除方法的前缀
38         mbsc.setAttribute(mbeanName, new Attribute("Name","杭州"));
39         mbsc.setAttribute(mbeanName, new Attribute("Age","1990"));
40         String age = (String)mbsc.getAttribute(mbeanName, "Age");
41         String name = (String)mbsc.getAttribute(mbeanName, "Name");
42         System.out.println("age=" + age + ";name=" + name);
43         
44         HelloMBean proxy = MBeanServerInvocationHandler.
45             newProxyInstance(mbsc, mbeanName, HelloMBean.class, false);
46         proxy.helloWorld();
47         proxy.helloWorld("migu");
48         proxy.getTelephone();
49         //invoke调用bean的方法,只针对非设置属性的方法
50         //例如invoke不能对getName方法进行调用
51         mbsc.invoke(mbeanName, "getTelephone", null, null);
52         mbsc.invoke(mbeanName, "helloWorld", 
53             new String[]{"I'll connect to JMX Server via client2"}, new String[]{"java.lang.String"});
54         mbsc.invoke(mbeanName, "helloWorld", null, null);
55     }
56 }

a、在35到41行,是对属性进行赋值和取值,这里咱们不能直接调用方法,而是经过setAttribute、getAttrubute方法来进行操做,则属性的首字母要大写。

b、对资源里面的方法进行操做有两种方式:一是经过代理直接调用方法;二是经过JAVA的反射注入的方式进行方法的调用。

下面咱们来看看执行结果,先执行agent,再执行客户端:

c、client的控制台打印结果:

 

d、agent控制台打印结果:

 6、Notification

 MBean之间的通讯是必不可少的,Notification就起到了在MBean之间沟通桥梁的做用。JMX 的通知由四部分组成:

一、Notification这个至关于一个信息包,封装了须要传递的信息

二、Notification broadcaster这个至关于一个广播器,把消息广播出。

三、Notification listener 这是一个监听器,用于监听广播出来的通知信息。

四、Notification filiter 这个一个过滤器,过滤掉不须要的通知。这个通常不多使用。

这里咱们使用平常打招呼的场景:jack与我偶遇,jack说:hi;我礼貌的回答:hello,jack。

这里咱们先分别建立两个资源:

package jmx;

/*
 * 该类名称必须与实现的接口的前缀保持一致(即MBean前面的名称
 */
public class Hello implements HelloMBean
{
    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 jmx;

/*
 * 接口名必须以MBean结尾
 */
public interface HelloMBean
{
    public String getName();   
    
    public void setName(String name);   
  
    public void printHello();   
  
    public void printHello(String whoName);
}
package jmx;

import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;

public class Jack extends NotificationBroadcasterSupport implements JackMBean
{
    private int seq = 0;
    public void hi()
    {
         //建立一个信息包
        Notification notify = 
            //通知名称;谁发起的通知;序列号;发起通知时间;发送的消息
            new Notification("jack.hi",this,++seq,System.currentTimeMillis(),"jack");
        sendNotification(notify);
    }

}
package jmx;

public interface JackMBean
{
    public void hi();
}

这里的类Jack不只实现了MBean接口,还继承了NotificationBroadcasterSupport。jack在这里建立并发送了一个消息包。

package jmx;

import javax.management.Notification;
import javax.management.NotificationListener;

public class HelloListener implements NotificationListener
{

    public void handleNotification(Notification notification, Object handback)
    {
        if(handback instanceof Hello)
        {
            Hello hello = (Hello)handback;
            hello.printHello(notification.getMessage());
        }
    }
    
}
package jmx;

import java.lang.management.ManagementFactory;

import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;

public class HelloAgent
{
    public static void main(String[] args) throws JMException, Exception
    {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName helloName = new ObjectName("yunge:name=Hello");    
        Hello hello=new Hello();          
        server.registerMBean(hello, helloName);  
        Jack jack = new Jack();
        server.registerMBean(jack, new ObjectName("jack:name=Jack"));
        jack.addNotificationListener(new HelloListener(), null, hello);
        Thread.sleep(500000);
    }
}

咱们用Jconsole来进行访问:

这里咱们能够看到有两个MBean,一个是yunge,一个是jack。咱们执行jack的hi方法后,去看下控制台上的打印信息;

 7、linux下利用JMX监控Tomcat

  利用JMX监控Tomcat,就是至关于部署在tomcat上的应用做为服务端,也就是被管理资源的对象。而后经过程序或者jconsole远程链接到该应用上来。远程链接须要服务器端提供ip和port。若是须要加密访问的话,还须要配置用户名、密码等参数。

主要是在tomcat下的文件catalina.sh中进行一些环境变量的配置配置:

 

-Dcom.sun.management.jmxremote=true                 相关 JMX 代理侦听开关

-Djava.rmi.server.hostname                                     服务器端的IP
-Dcom.sun.management.jmxremote.port=29094             相关 JMX 代理侦听请求的端口

-Dcom.sun.management.jmxremote.ssl=false              指定是否使用 SSL 通信

-Dcom.sun.management.jmxremote.authenticate=false     指定是否须要密码验证这样就能够经过客户端或者jconsole对tomcat进行监控。

相关文章
相关标签/搜索