JMX介绍 之一 JMX基础和Standard MBeans
由于需要解决一个JMX的问题,回顾并总结了一下JMX相关的知识。
?
1. JMX架构简单来说,JMX的组成以及关系如下:
JMX客户端代码 ? ---> ? JMX connector ---> ? {.........网络........} ---> MBean Server ---> ?MBean(被调用对象)
其中JMX connector和MBean Server在JDK里面已经提供,我们只需要实现被调用对象,已经调用的代码。而且,在JDK里面已经提供了非常多的MBean,可以直接使用。
?
2. 启用JMXJMX的用处就是远程监控和管理。最普遍的就是JVM的运行状态的监控。
在我们的系统中,也可以有很多用处,例如,需要对一些运行状态或者参数进行查看或修改,特别是系统调优时,有几种处理方法或算法,可以通过JMX提供方便的修改,可以实时的看到结果。
?
要启用JMX,只需要在java的启动参数里面加上下面的参数
-Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
?
如果是tomcat,就用ctalena.sh start -Dcom...... ?这样就可以。
?
然后就可以运行jconsole,连接到远程的tomcat server。
?
3. MBean类型在JMX标准中,定义了5中类型的MBean:
?
?
至于各种类型是什么意思,在JDK的tutorial中没有说明,我也懒得找其他资料,我觉得只是使用的话,就只需要知道标准的MBeans和MXBeans就可以。Standard MBeans就是最基础的MBean,通过它可以了解MBea的工作方式,对MBean有一个了解。
如果大家了解远程调用,就应该知道,一般远程的客户端的代码中要包含所要调用对象的存根(stub),也就是要包含远程对象的interface。在使用Standard MBeans时,我们就需要在调用端包含被调用的远程的MBean对象的接口。
?
而在MXBeans类型中,客户端不需要知道远程对象的类型,更不需要包含它的接口,就能够通过一些MXBeans中预定义的集合类型,就能够知道如何调用远程对象,需要的参数以及类型等。
在后面的例子中就可以看到MXBeans和普通MBean的区别。
?
?
3. Standard MBeans使用这种类型的MBeans,首先,需要定义一个接口,也就是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); } ?在这个接口中,定义了5个方法,但是,只有add和syHello两个方法才会被当做是这个Mbean的接口。
根据JMX标准的定义,所有的getter方法,即以get开头,不返回void的方法,都不是MBean对外提供的方法。即使get方法没有对应的成员变量,只是一个普通的get方法,也会被认为是getter方法,而不是MBean的接口。同样,setter方法也是。
然后,根据上面的接口类中的getter和setter方法定义,JMX对认为这个MBean对象有两个属性,即name和cacheSize。
?
接下来就是这个接口的实现:
?
?
package com.example.mbeans; 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; } ?实现没什么可说的。
?
然后就是注册这个MBean:
?
?
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.mbeans:type=Hello"); Hello mbean = new Hello(); mbs.registerMBean(mbean, name); System.out.println("Waiting forever..."); Thread.sleep(Long.MAX_VALUE); } } ?它用ManagementFactory生成一个MBean server,会在一个端口上监听JMX请求,我这里是63560,不知道这个默认端口是不是固定的。但是,这个端口不是用来远程连接的。
?
编译好所有的类以后,运行Main,然后,就可以打开jconsole,就能看到有一个本地的Main进程,连接上以后,就能看到有两个属性,以及2个操作。
?
我们还可以在java里面使用发送jmx请求的方式来调用MBeanserver里面的对象方法。要在java程序中使用,我们就需要在运行Main的时候,加上JMX的参数,这样才能连接。
?
下面就是client的代码:
?
import javax.management.JMX;import javax.management.MBeanServerConnection;import javax.management.ObjectName;import javax.management.remote.JMXConnector;import javax.management.remote.JMXConnectorFactory;import javax.management.remote.JMXServiceURL;public class HelloClient {public static void main(String[] args) {try {JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi");JMXConnector jmxc = JMXConnectorFactory.connect(url, null);MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();ObjectName mbeanName = new ObjectName("com.example.mbeans:type=Hello");HelloMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName,HelloMBean.class, true);System.out.println("CacheSize = " + mbeanProxy.getCacheSize());mbeanProxy.setCacheSize(150);System.out.println("CacheSize = " + mbeanProxy.getCacheSize());System.out.println("Invoke sayHello() in Hello MBean...");mbeanProxy.sayHello();System.out.println("add(2, 3) = " + mbeanProxy.add(2, 3));} catch (Exception e) {e.printStackTrace();}}}??
注意,上面的代码中,端口是启用JMX时配置的端口,创建mbeanName时使用的名字“com.example.mbeans:type=Hello” 必须和Main中注册时使用的名字一致。
?
还有,我们需要import 进来HelloMBean接口类,一般情况下,是需要把远程对象的接口打包成一个jar,然后包含在客户端的lib目录中。在上面的client代码中,因为我的HelloMbean类是直接复制过来的,而且都在同一个包下,所以没有显式的import进来。
?