1月 062015
 

JMX学习(二)——通知 ,分享:

Notification   通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知.

 

 

这里写一个简单的Server配置例子, 首先定义我们的MBean接口:

 

 

Java代码 复制代码

  1. package com.haitao.jmx.mbeans.server;   
  2.   
  3. /**  
  4.  *   
  5.  * Server Configure MBean  
  6.  *   
  7.  * @author haitao.tu  
  8.  *  
  9.  */  
  10. public interface ServerConfigureMBean {   
  11.   
  12.     public void setPort(int port);   
  13.        
  14.     public int getPort();   
  15.        
  16.     public void setHost(String host);   
  17.        
  18.     public String getHost();   
  19.        
  20. }  
[java] view plaincopy

  1. package com.haitao.jmx.mbeans.server;  
  2.   
  3. /** 
  4.  *  
  5.  * Server Configure MBean 
  6.  *  
  7.  * @author haitao.tu 
  8.  * 
  9.  */  
  10. public interface ServerConfigureMBean {  
  11.   
  12.     public void setPort(int port);  
  13.       
  14.     public int getPort();  
  15.       
  16.     public void setHost(String host);  
  17.       
  18.     public String getHost();  
  19.       
  20. }  

 

 

 

接着,我们会想第一节那样,去实现这个MBean接口,并且继承NotificationBroadcasterSupport,来提供广播服务:

 

 

Java代码 复制代码

  1. package com.haitao.jmx.mbeans.server;   
  2.   
  3. import java.util.concurrent.atomic.AtomicLong;   
  4.   
  5. import javax.management.AttributeChangeNotification;   
  6. import javax.management.NotificationBroadcasterSupport;   
  7.   
  8. /**  
  9.  * Server Configure  
  10.  *   
  11.  * @author haitao.tu  
  12.  *  
  13.  */  
  14. public class ServerConfigure extends NotificationBroadcasterSupport implements ServerConfigureMBean {   
  15.        
  16.     private AtomicLong sequenceNumber = new AtomicLong(1);   
  17.   
  18.     private int port;   
  19.   
  20.     private String host;   
  21.   
  22.     @Override  
  23.     public void setPort(int port) {   
  24.         int oldPort = this.port;   
  25.         this.port = port;   
  26.         AttributeChangeNotification notification = new AttributeChangeNotification(   
  27.                 this,   
  28.                 sequenceNumber.getAndIncrement(),   
  29.                 System.currentTimeMillis(),   
  30.                 AttributeChangeNotification.ATTRIBUTE_CHANGE,   
  31.                 “Server Port Change”,   
  32.                 “java.lang.Integer”,   
  33.                 oldPort + “”,   
  34.                 this.port + “”  
  35.                 );   
  36.         super.sendNotification(notification);   
  37.     }   
  38.   
  39.     @Override  
  40.     public void setHost(String host) {   
  41.         String oldHost = this.host;   
  42.         this.host = host;   
  43.         AttributeChangeNotification notification = new AttributeChangeNotification(   
  44.                 this,   
  45.                 sequenceNumber.getAndIncrement(),   
  46.                 System.currentTimeMillis(),   
  47.                 AttributeChangeNotification.ATTRIBUTE_CHANGE,   
  48.                 “Server Host Change”,   
  49.                 “java.lang.String”,   
  50.                 oldHost,   
  51.                 this.host   
  52.                 );   
  53.         super.sendNotification(notification);   
  54.     }   
  55.   
  56.     @Override  
  57.     public int getPort() {   
  58.         return port;   
  59.     }   
  60.   
  61.     @Override  
  62.     public String getHost() {   
  63.         return host;   
  64.     }   
  65.   
  66. }  
[java] view plaincopy

  1. package com.haitao.jmx.mbeans.server;  
  2.   
  3. import java.util.concurrent.atomic.AtomicLong;  
  4.   
  5. import javax.management.AttributeChangeNotification;  
  6. import javax.management.NotificationBroadcasterSupport;  
  7.   
  8. /** 
  9.  * Server Configure 
  10.  *  
  11.  * @author haitao.tu 
  12.  * 
  13.  */  
  14. public class ServerConfigure extends NotificationBroadcasterSupport implements ServerConfigureMBean {  
  15.       
  16.     private AtomicLong sequenceNumber = new AtomicLong(1);  
  17.   
  18.     private int port;  
  19.   
  20.     private String host;  
  21.   
  22.     @Override  
  23.     public void setPort(int port) {  
  24.         int oldPort = this.port;  
  25.         this.port = port;  
  26.         AttributeChangeNotification notification = new AttributeChangeNotification(  
  27.                 this,  
  28.                 sequenceNumber.getAndIncrement(),  
  29.                 System.currentTimeMillis(),  
  30.                 AttributeChangeNotification.ATTRIBUTE_CHANGE,  
  31.                 “Server Port Change”,  
  32.                 “java.lang.Integer”,  
  33.                 oldPort + “”,  
  34.                 this.port + “”  
  35.                 );  
  36.         super.sendNotification(notification);  
  37.     }  
  38.   
  39.     @Override  
  40.     public void setHost(String host) {  
  41.         String oldHost = this.host;  
  42.         this.host = host;  
  43.         AttributeChangeNotification notification = new AttributeChangeNotification(  
  44.                 this,  
  45.                 sequenceNumber.getAndIncrement(),  
  46.                 System.currentTimeMillis(),  
  47.                 AttributeChangeNotification.ATTRIBUTE_CHANGE,  
  48.                 “Server Host Change”,  
  49.                 “java.lang.String”,  
  50.                 oldHost,  
  51.                 this.host  
  52.                 );  
  53.         super.sendNotification(notification);  
  54.     }  
  55.   
  56.     @Override  
  57.     public int getPort() {  
  58.         return port;  
  59.     }  
  60.   
  61.     @Override  
  62.     public String getHost() {  
  63.         return host;  
  64.     }  
  65.   
  66. }  

 

 

在setPort与setHos方法中,首先new了一个AttributeChangeNotification,这个类是javax.management.Notification的子类,而javax.management.Notification

这个类又是java.util.EventObject的子类,由此可以证实上边所说的,JMX通知机制使用了观察者设计模式.

 

javax.management.Notification是一个JMX的通知核心类,将来需要扩展或者其他JMX自带的消息,均集成自此类.

 

AttributeChangeNotification根据类名可知,是一个属性改变的通知,造方法参数如下:

 

 

Object source,                 // 事件源,一直传递到java.util.EventObject的source

long sequenceNumber,   // 通知序号,标识每次通知的计数器

long timeStamp,              // 通知发出的时间戳 

String msg,                     // 通知发送的message

String attributeName,     // 被修改属性名

String attributeType,      // 被修改属性类型

Object oldValue,             // 被修改属性修改以前的值

Object newValue            // 被修改属性修改以后的值

 

 

根据观察者模式,由事件与广播组成,所以这里继承了NotificationBroadcasterSupport,来提供广播机制,

 

调用NotificationBroadcasterSupportr的sendNotification(notification) 发送广播,广播会根据注册的观察者

 

来对观察者进行逐一通知.

 

 

sendNotification 在JDK1.6是通过Executor来发送通知,默认调用线程同步发送:

 

 

Java代码 复制代码

  1. public NotificationBroadcasterSupport(Executor executor,   
  2.                       MBeanNotificationInfo… info) {   
  3.     this.executor = (executor != null) ? executor : defaultExecutor;   
  4.   
  5.     notifInfo = info == null ? NO_NOTIFICATION_INFO : info.clone();   
  6.     }  
[java] view plaincopy

  1. public NotificationBroadcasterSupport(Executor executor,  
  2.                       MBeanNotificationInfo… info) {  
  3.     this.executor = (executor != null) ? executor : defaultExecutor;  
  4.   
  5.     notifInfo = info == null ? NO_NOTIFICATION_INFO : info.clone();  
  6.     }  

 

 

 

Java代码 复制代码

  1. private final static Executor defaultExecutor = new Executor() {   
  2.         // DirectExecutor using caller thread   
  3.         public void execute(Runnable r) {   
  4.         r.run();   
  5.         }   
  6.     };   
[java] view plaincopy

  1. private final static Executor defaultExecutor = new Executor() {  
  2.         // DirectExecutor using caller thread  
  3.         public void execute(Runnable r) {  
  4.         r.run();  
  5.         }  
  6.     };   

 

 

如果想用异步发送通知,大家可以在构造方法中传入异步执行的Executor , 例如 ThreadPoolExecutor.

 

接下来,还得写一个观察者,来接受我们送出的通知:

 

 

Java代码 复制代码

  1. package com.haitao.jmx.mbeans.server;   
  2.   
  3. import javax.management.Notification;   
  4. import javax.management.NotificationListener;   
  5.   
  6. /**  
  7.  * Server Configure Notification Listener  
  8.  *   
  9.  * @author haitao.tu  
  10.  *   
  11.  */  
  12. public class ServerConfigureNotificationListener implements  
  13.         NotificationListener {   
  14.   
  15.     @Override  
  16.     public void handleNotification(Notification notification, Object handback) {   
  17.         log(“SequenceNumber:” + notification.getSequenceNumber());   
  18.         log(“Type:” + notification.getType());   
  19.         log(“Message:” + notification.getMessage());   
  20.         log(“Source:” + notification.getSource());   
  21.         log(“TimeStamp:” + notification.getTimeStamp());   
  22.     }   
  23.   
  24.     private void log(String message) {   
  25.         System.out.println(message);   
  26.     }   
  27.   
  28. }  
[java] view plaincopy

  1. package com.haitao.jmx.mbeans.server;  
  2.   
  3. import javax.management.Notification;  
  4. import javax.management.NotificationListener;  
  5.   
  6. /** 
  7.  * Server Configure Notification Listener 
  8.  *  
  9.  * @author haitao.tu 
  10.  *  
  11.  */  
  12. public class ServerConfigureNotificationListener implements  
  13.         NotificationListener {  
  14.   
  15.     @Override  
  16.     public void handleNotification(Notification notification, Object handback) {  
  17.         log(“SequenceNumber:” + notification.getSequenceNumber());  
  18.         log(“Type:” + notification.getType());  
  19.         log(“Message:” + notification.getMessage());  
  20.         log(“Source:” + notification.getSource());  
  21.         log(“TimeStamp:” + notification.getTimeStamp());  
  22.     }  
  23.   
  24.     private void log(String message) {  
  25.         System.out.println(message);  
  26.     }  
  27.   
  28. }  

 

这里只是简单输出了通知内容, 在这个类中我们实现NotificationListener接口,可以看出该接口中只有一个方法,

就是处理消息,顺藤摸瓜,在看一下NotificationListener的接口代码:

 

 

Java代码 复制代码

  1. package javax.management;   
  2.   
  3.   
  4. import java.util.EventListener;   
  5.   
  6.   
  7. /**  
  8.  * Should be implemented by an object that wants to receive notifications.  
  9.  *  
  10.  * @since 1.5  
  11.  */  
  12. public interface NotificationListener extends java.util.EventListener   {    
  13.   
  14.     /**  
  15.     * Invoked when a JMX notification occurs.  
  16.     * The implementation of this method should return as soon as possible, to avoid  
  17.     * blocking its notification broadcaster.  
  18.     *  
  19.     * @param notification The notification.      
  20.     * @param handback An opaque object which helps the listener to associate information 
  21.     * regarding the MBean emitter. This object is passed to the MBean during the  
  22.     * addListener call and resent, without modification, to the listener. The MBean object  
  23.     * should not use or modify the object.   
  24.     *  
  25.     */  
  26.     public void handleNotification(Notification notification, Object handback) ;   
  27. }  
[java] view plaincopy

  1. package javax.management;  
  2.   
  3.   
  4. import java.util.EventListener;  
  5.   
  6.   
  7. /** 
  8.  * Should be implemented by an object that wants to receive notifications. 
  9.  * 
  10.  * @since 1.5 
  11.  */  
  12. public interface NotificationListener extends java.util.EventListener   {   
  13.   
  14.     /** 
  15.     * Invoked when a JMX notification occurs. 
  16.     * The implementation of this method should return as soon as possible, to avoid 
  17.     * blocking its notification broadcaster. 
  18.     * 
  19.     * @param notification The notification.     
  20.     * @param handback An opaque object which helps the listener to associate information 
  21.     * regarding the MBean emitter. This object is passed to the MBean during the 
  22.     * addListener call and resent, without modification, to the listener. The MBean object  
  23.     * should not use or modify the object.  
  24.     * 
  25.     */  
  26.     public void handleNotification(Notification notification, Object handback) ;  
  27. }  

 

可以很清楚的看出继承了java.util.EventListener接口,又一次证实了,JMX通知机制是观察者模式的衍生产品.

 

好了,所有的功能代码都写完了,下边需要测试一JMX的通知机制:

 

这里还需要写一个测试用例来支持:

 

 

Java代码 复制代码

  1. package com.haitao.jmx.mbeans.server;   
  2.   
  3. import java.lang.management.ManagementFactory;   
  4.   
  5. import javax.management.MBeanServer;   
  6. import javax.management.ObjectName;   
  7.   
  8. public class ServerStartup {   
  9.   
  10.     public static void main(String[] args) throws Exception {   
  11.         // 创建MBeanServer   
  12.         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();   
  13.         // 新建MBean ObjectName, 在MBeanServer里标识注册的MBean   
  14.         ObjectName name = new ObjectName(“com.haitao.jmx.mbeans.server:type=ServerConfigure”);   
  15.         // 创建MBean   
  16.         ServerConfigure mbean = new ServerConfigure();   
  17.         // 在MBeanServer里注册MBean, 标识为ObjectName(com.haitao.jmx.mbeans.server:type=ServerConfigure)   
  18.         mbs.registerMBean(mbean, name);   
  19.         // 自定义观察者   
  20.         ServerConfigureNotificationListener listener = new ServerConfigureNotificationListener();   
  21.         // 加入MBeanServer   
  22.         mbs.addNotificationListener(name, listener, nullnull);   
  23.         Thread.sleep(Long.MAX_VALUE);   
  24.     }   
  25.        
  26. }  
[java] view plaincopy

  1. package com.haitao.jmx.mbeans.server;  
  2.   
  3. import java.lang.management.ManagementFactory;  
  4.   
  5. import javax.management.MBeanServer;  
  6. import javax.management.ObjectName;  
  7.   
  8. public class ServerStartup {  
  9.   
  10.     public static void main(String[] args) throws Exception {  
  11.         // 创建MBeanServer  
  12.         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();  
  13.         // 新建MBean ObjectName, 在MBeanServer里标识注册的MBean  
  14.         ObjectName name = new ObjectName(“com.haitao.jmx.mbeans.server:type=ServerConfigure”);  
  15.         // 创建MBean  
  16.         ServerConfigure mbean = new ServerConfigure();  
  17.         // 在MBeanServer里注册MBean, 标识为ObjectName(com.haitao.jmx.mbeans.server:type=ServerConfigure)  
  18.         mbs.registerMBean(mbean, name);  
  19.         // 自定义观察者  
  20.         ServerConfigureNotificationListener listener = new ServerConfigureNotificationListener();  
  21.         // 加入MBeanServer  
  22.         mbs.addNotificationListener(name, listener, nullnull);  
  23.         Thread.sleep(Long.MAX_VALUE);  
  24.     }  
  25.       
  26. }  

 

最后,我们开始验证成果:

 

1.打开%JAVA_HOME%/bin/jconsole连接到本地进程:

 

 

jconsole

 

2. 进入MBean选项框, 点击左边的树,打开通知:

 

 

 

3. 订阅通知

 

 

 

4. 修改属性,产生通知

 

 

 

 

5. 验证通知

 

 

 

OK, 学习笔记二写完了,回想下一, 

 

1. JMX中要定义接口必须以xxxMBean的规范定义

2. 得有类实现xxxMBean接口

3. 在实现类中可以继承NotificationBroadcasterSupport来支持通知机制

4. 可以通过jconsole来验证

 转自:http://blog.csdn.net/qiao000_000/article/details/6061813

1月 062015
 

关于java JMX很好的入门文章,分享下:

JMX        即 Java Management Extensions   Java管理扩展
MBean   即 managed beans                         被管理的Beans
一个MBean是一个被管理的Java对象,有点类似于JavaBean,一个设备、一个应用或者任何资源都可以被表示为MBean,MBean会暴露一个接口对外,这个接口可以读取或者写入一些对象中的属性,通常一个MBean需要定义一个接口,以MBean结尾, 例如: EchoMBean, 格式为XXXMBean,这个是规范,必须得遵守。例如:

 

 

Java代码 复制代码

  1. package com.haitao.jmx;   
  2.   
  3. /**  
  4.  * JMX MBean Interface  
  5.  *   
  6.  * @author haitao.tu  
  7.  *   
  8.  */  
  9. public interface EchoMBean {   
  10.   
  11.     public void print(String yourName);   
  12.   
  13. }  
[java] view plaincopy

  1. package com.haitao.jmx;  
  2.   
  3. /** 
  4.  * JMX MBean Interface 
  5.  *  
  6.  * @author haitao.tu 
  7.  *  
  8.  */  
  9. public interface EchoMBean {  
  10.   
  11.     public void print(String yourName);  
  12.   
  13. }  

 

 

很简单的,在EchoMBean接口中,定义了一个print方法,带一个yourName的String类型参数,只有接口貌似没什么用,下边我们实现这个接口

 

 

Java代码 复制代码

  1. package com.haitao.jmx;   
  2.   
  3. /**  
  4.  * Implements of JMX EchoMBean  
  5.  *   
  6.  * @author haitao.tu  
  7.  *  
  8.  */  
  9. public class Echo implements EchoMBean {   
  10.   
  11.     @Override  
  12.     public void print(String yourName) {   
  13.         System.out.println(“Hi “ + yourName + “!”);   
  14.     }   
  15.        
  16. }  
[java] view plaincopy

  1. package com.haitao.jmx;  
  2.   
  3. /** 
  4.  * Implements of JMX EchoMBean 
  5.  *  
  6.  * @author haitao.tu 
  7.  * 
  8.  */  
  9. public class Echo implements EchoMBean {  
  10.   
  11.     @Override  
  12.     public void print(String yourName) {  
  13.         System.out.println(“Hi “ + yourName + “!”);  
  14.     }  
  15.       
  16. }  

 


Echo实现了EchoMBean接口,很简单我们只是print了hi yourName!
按照JMX的定义,是被管理的对象,现在我们只是定义了该对象,并没有被管理,接着我们让这个Echo类的实例对象被管理起来:

Java代码 复制代码

  1. package com.haitao.jmx;   
  2.   
  3. import java.lang.management.ManagementFactory;   
  4.   
  5. import javax.management.MBeanServer;   
  6. import javax.management.ObjectName;   
  7.   
  8. /**  
  9.  * JMX App Demo  
  10.  *   
  11.  * @author haitao.tu  
  12.  */  
  13. public class App {   
  14.        
  15.     public static void main(String[] args) throws Exception {   
  16.         // 创建MBeanServer   
  17.         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();   
  18.            
  19.         // 新建MBean ObjectName, 在MBeanServer里标识注册的MBean   
  20.         ObjectName name = new ObjectName(“com.haitao.jmx:type=Echo”);   
  21.            
  22.         // 创建MBean   
  23.         Echo mbean = new Echo();   
  24.            
  25.         // 在MBeanServer里注册MBean, 标识为ObjectName(com.tenpay.jmx:type=Echo)   
  26.         mbs.registerMBean(mbean, name);   
  27.   
  28.            
  29.         // 在MBeanServer里调用已注册的EchoMBean的print方法   
  30.         mbs.invoke(name, “print”new Object[] { “haitao.tu”}, new String[] {“java.lang.String”});   
  31.            
  32.         Thread.sleep(Long.MAX_VALUE);   
  33.     }   
  34.   
  35. }  
[java] view plaincopy

  1. package com.haitao.jmx;  
  2.   
  3. import java.lang.management.ManagementFactory;  
  4.   
  5. import javax.management.MBeanServer;  
  6. import javax.management.ObjectName;  
  7.   
  8. /** 
  9.  * JMX App Demo 
  10.  *  
  11.  * @author haitao.tu 
  12.  */  
  13. public class App {  
  14.       
  15.     public static void main(String[] args) throws Exception {  
  16.         // 创建MBeanServer  
  17.         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();  
  18.           
  19.         // 新建MBean ObjectName, 在MBeanServer里标识注册的MBean  
  20.         ObjectName name = new ObjectName(“com.haitao.jmx:type=Echo”);  
  21.           
  22.         // 创建MBean  
  23.         Echo mbean = new Echo();  
  24.           
  25.         // 在MBeanServer里注册MBean, 标识为ObjectName(com.tenpay.jmx:type=Echo)  
  26.         mbs.registerMBean(mbean, name);  
  27.   
  28.           
  29.         // 在MBeanServer里调用已注册的EchoMBean的print方法  
  30.         mbs.invoke(name, “print”new Object[] { “haitao.tu”}, new String[] {“java.lang.String”});  
  31.           
  32.         Thread.sleep(Long.MAX_VALUE);  
  33.     }  
  34.   
  35. }  

 


1. 首先我们在App类中向ManagementFactory申请了一个MBeanServer对象
2. 接着我们即然要使Echo的实例对象被管理起来,我们就需要给这个对象一个标识,这个标识是ObjectName.注意这个ObjectName构造函数,这里使用了(包名:type=类名)的形式.
3. 然后我们通过mbs.registerMBean方法注册了echo,并传入了ObjectName在MBeanServer中标识该MBean.
4. 随后我们通过mbs.invoke方法调用了已经注册的Echo的print方法,通过ObjectName找到该MBean, 并通过最后两个参数,传入print方法执行的参数,与参数的类型。
5. 最后我们sleep主线程,等待其他线程的调用.
通过这个例子我们可以看出,MBean的好处,在Echo的实例对象未被管理之前,我们只能通过Echo对象的句柄,来调用Echo里的public方法,在被管理之后,我们可以通过MBeanServer的句柄mbs来调用Echo对象的print方法。
更详细的内容,我们可以通过JDK自带工具jconsole或者 VisualVM 来查看MBean:

 

1.jconsole

 

jconsole的位置在%JAVA_HOME%/bin/jconsole.exe

 

开后会看到:

 

 

选择com.haitao.jmx.App,然后点击连接, 选择MBean后会看到

 

这里可以直接调用Echo的print方法, 当然大家看到这里边还有好多其他的table,包括概述、内存、线程、类、VM摘要、MBean。

 

 

2.VisualVM

 

VisualVM的位置在%JAVA_HOME%/bin/jvisualvm.exe

 

打开后经过连接我们会看到类似的效果:

 

 

转自:http://blog.csdn.net/qiao000_000/article/details/6061808

 Posted by at 上午7:42  Tagged with: ,
4月 242014
 

IBM的文章就是不同反响,强烈推荐!

前言

在 Java 程序的运行过程中,对 JVM 和系统的监测一直是 Java 开发人员在开发过程所需要的。一直以来,Java 开发人员必须通过一些底层的 JVM API,比如 JVMPI 和 JVMTI 等,才能监测 Java 程序运行过程中的 JVM 和系统的一系列情况,这种方式一直以来被人所诟病,因为这需要大量的 C 程序和 JNI 调用,开发效率十分低下。于是出现了各种不同的专门做资源管理的程序包。为了解决这个问题,Sun 公司也在其 Java SE 5 版本中,正式提出了 Java 管理扩展(Java Management Extensions,JMX)用来管理检测 Java 程序(同时 JMX 也在 J2EE 1.4 中被发布)。

JMX 的提出,让 JDK 中开发自检测程序成为可能,也提供了大量轻量级的检测 JVM 和运行中对象 / 线程的方式,从而提高了 Java 语言自己的管理监测能力。

JMX 和系统管理

管理系统(Management System)

要了解 JMX,我们就必须对当前的 IT 管理系统有一个初步的了解。随着企业 IT 规模的不断增长,IT 资源(IT resource)数量不断增加,IT 资源的分布也越来越分散。可以想象,甚至对于一家只有几百台 PC 公司的 IT 管理人员来说,分发一个安全补丁并且保证其在每台 PC 上的安装,如果只依赖人工来完成那简直就是一场噩梦。这样,IT 管理系统就应运而生。

然而,CPU、网卡、存储阵列是 IT 资源;OS、MS Office、Oracle database、IBM Websphere 也是 IT 资源。IT 管理系统若要对这些 IT 资源进行管理,就必须对这些管理对象有所了解:形形色色的 IT 资源就像是说着不同语言的人:Oralce 数据库表达内存紧张的方式和 Window XP 是绝然不同的, 而 IT 管理系统就像建造通天塔的经理,必须精通所有的语言, 这几乎是一个不可能完成的任务。难道 IT 管理系统是另外一个通天塔吗?当然不是!其实我们只要给每个 IT 资源配个翻译就可以了。

管理系统的构架

图 1. 管理系统构架

图 1. 管理系统构架

上图分析了管理系统的基本构架模式。其中 Agent / SubAgent 起到的就是翻译的作用:把 IT 资源报告的消息以管理系统能理解的方式传送出去。

也许读者有会问,为什么需要 Agent 和 SubAgent 两层体系呢?这里有两个现实的原因:

  1. 管理系统一般是一个中央控制的控制软件,而 SubAgent 直接监控一些资源,往往和这些资源分布在同一物理位置。当这些 SubAgent 把状态信息传输到管理系统或者传达管理系统的控制指令的时候,需要提供一些网络传输的功能。
  2. 管理系统的消息是有一定规范的,消息的翻译本身是件复杂而枯燥的事情。

一般来说,管理系统会将同一物理分布或者功能类似的 SubAgent 分组成一组,由一个共用的 Agent 加以管理。在这个 Agent 里封装了 1 和 2 的功能。

JMX 和管理系统

JMX 既是 Java 管理系统的一个标准,一个规范,也是一个接口,一个框架。图 2 展示了 JMX 的基本架构。

图 2. JMX 构架

图 2. JMX 构架

和其它的资源系统一样,JMX 是管理系统和资源之间的一个接口,它定义了管理系统和资源之间交互的标准。javax.management.MBeanServer实现了 Agent 的功能,以标准的方式给出了管理系统访问 JMX 框架的接口。而javax.management.MBeans实现了 SubAgent 的功能,以标准的方式给出了 JMX 框架访问资源的接口。而从类库的层次上看,JMX 包括了核心类库 java.lang.management和 javax.management包。java.lang.management包提供了基本的 VM 监控功能,而javax.management包则向用户提供了扩展功能。

JMX 的基本框架

JMX 使用了 Java Bean 模式来传递信息。一般说来,JMX 使用有名的 MBean,其内部包含了数据信息,这些信息可能是:应用程序配置信息、模块信息、系统信息、统计信息等。另外,MBean 也可以设立可读写的属性、直接操作某些函数甚至启动 MBean 可发送的 notification 等。MBean 包括 Standard,MXBean,Dynamic,Model,Open 等几种分类,其中最简单是标准 MBean 和 MXBean,而我们使用得最多的也是这两种。MXBean 主要是 java.lang.management使用较多,将在下一节中介绍。我们先了解其他一些重要的 MBean 的种类。

标准 MBean

标准 MBean 是最简单的一类 MBean,与动态 Bean 不同,它并不实现 javax.management包中的特殊的接口。说它是标准 MBean, 是因为其向外部公开其接口的方法和普通的 Java Bean 相同,是通过 lexical,或者说 coding convention 进行的。下面我们就用一个例子来展现,如何实现一个标准 MBean 来监控某个服务器 ServerImpl 状态的。ServerImpl 代表了用来演示的某个 Server 的实现:

 package standardbeans;
 public class ServerImpl {
    public final long startTime;
    public ServerImpl() {
        startTime = System.currentTimeMillis();
    }
 }

然后,我们打算使用一个标准 MBean,ServerMonitor 来监控 ServerImpl:

 package standardbeans;
 public class ServerMonitor implements ServerMonitorMBean {
    private final ServerImpl target;
    public ServerMonitor(ServerImpl target){
        this.target = target;
    }
    public long getUpTime(){
        return System.currentTimeMillis() - target.startTime;
    }
 }

这里的 ServerMonitorBean 又是怎么回事呢? MXBean 规定了标准 MBean 也要实现一个接口,所有向外界公开的方法都要在这个接口中声明。否则,管理系统就不能从中获得相应的信息。此外,该接口的名字也有一定的规范:即在标准 MBean 类名之后加上“MBean”后缀。若 MBean 的类名叫做 MBeansName 的话,对应的接口就要叫做 MBeansNameMBean。

对于管理系统来说,这些在 MBean 中公开的方法,最终会被 JMX 转化成属性(Attribute)、监听(Listener)和调用(Invoke)的概念。如果读者对 Java Bean 有一些了解的话,不难看出,public long getUpTime()对应了 Bean 中的一个称为“upTime”的只读属性。

下面我们就看一个模拟管理系统的例子:

 package standardbeans;
 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);
    }
 }

JMX 的核心是 MBServer。Java SE 已经提供了一个默认实现,可以通过 MBServerFactory.createMBeanServer()获得。每个资源监控者(MBean)一般都会有名称(ObjectName), 登记在 MBServer 内部的一个 Repository 中。注意,这个 ObjectName 对于每一个 MBServer 必须是唯一的,只能对应于一个 MBean。(读者有兴趣的话,可以试着再给 mBeanServer 注册一个同名的 objectName,看看会怎么样。) 上述例子是在 init()方法中完成向 MBeanServer 注册工作的。

在管理过程中,管理系统并不与资源或者 SubAgent 直接打交道,也就是说,这里不会直接引用到 MBean。而是通过 MBeanServer 的getAttribute方法取得对应 MBean 的属性的。

动态 MBean

但是对于很多已有的 SubAgent 实现,其 Coding Convention 并不符合标准 MBean 的要求。重构所有这些 SubAgent 以符合标准 MBean 标准既费力也不实际。JMX 中给出了动态(Dynamic) MBean 的概念,MBServer 不再依据 Coding Convention 而是直接查询动态 MBean 给出的元数据(meta data)以获得 MBean 的对外接口。

 package dynamicbeans;

 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;
    }
 }

其它动态 MBean

另外还有两类 MBean:Open MBean 和 Model MBean。实际上它们也都是动态 MBean。

Open MBean 与其它动态 MBean 的唯一区别在于,前者对其公开接口的参数和返回值有所限制 —— 只能是基本类型或者javax.management.openmbean包内的 ArrayType、CompositeType、TarbularType 等类型。这主要是考虑到管理系统的分布,很可能远端管理系统甚至 MBServer 层都不具有 MBean 接口中特殊的类。

Model Bean

然而,普通的动态 Bean 通常缺乏一些管理系统所需要的支持:比如持久化 MBean 的状态、日志记录、缓存等等。如果让用户去一一实现这些功能确实是件枯燥无聊的工作。为了减轻用户的负担,JMX 提供商都会提供不同的 ModelBean 实现。其中有一个接口是 Java 规范中规定所有厂商必须实现的:javax.management.modelmbean.RequiredModelBean。通过配置 Descriptor 信息,我们可以定制这个 Model Bean, 指定哪些 MBean 状态需要记入日志、如何记录以及是否缓存某些属性、缓存多久等等。这里,我们以 RequiredModelBean 为例讨论 ModelBean。比如,我们先来看一个例子,首先是 server 端:

 package modelmbean;

 public class Server {

	 private long startTime;

	 public Server() { 	 }

	 public int start(){
		 startTime = System.currentTimeMillis();
		 return 0;
	 }

	 public long getUpTime(){
		 return System.currentTimeMillis() - startTime;
	 }
 }

然后我们对它的监测如下:

package modelmbean;

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;
    }

很明显,和其它 MBean 类似,使用 Model MBean 的过程也是下面几步:

  1. 创建一个 MBServer:mBeanServe
  2. 获得管理资源用的 MBean:serverBean
  3. 给这个 MBean 一个 ObjectName:serverMBeanName
  4. 将 serverBean 以 serverMBeanName 注册到 mBeanServer 上去

唯一不同的是,ModelMBean 需要额外两步 :

 1.serverMBean.setModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName));
 2.serverMBean.setManagedResource(server, "ObjectReference");

第一步用于提供 serverMBean 的元数据,主要包括以下两类

  1. 类似于普通的动态 MBean,需要 MBean 的 Attribute、Invocation、Notification 的类型 / 反射信息,诸如返回类型、参数类型和相关的 get/set 方法等。这里将不再赘述。
  2. 关于缓存、持久化以及日志等的策略。后面我们将介绍一些这方面的信息。

第二步指出了 ServerMBean 管理的对象,也就是说,从元数据中得到的 Method 将施加在哪个 Object 上。需要指出的是setManagedResource(Object o, String type);中第二个参数是 Object 类型,可以是 “ObjectReference”、”Handle”、”IOR”、”EJBHandle” 或 “RMIReference”。目前 SE 中的实现只支持 “ObjectReference”。笔者认为后面几种类型是为了将来 JMX 管理对象扩展而设定的,可能将来 Model Bean 不仅可以管理 Plain Java Object(POJO),还可能管理 Native Resource, 并给诸如 EJB 和 RMI 对象的管理提供更多的特性。

Model Bean 与普通动态 Bean 区别在于它的元数据类型 ModelMBeanInfo 扩展了前者的 MBeanInfo,使得 ModelMBeanOperationInfo、ModelMBeanConstructor_Info、ModelMBeanAttributeInfo 和 ModelMBeanNotificationInfo 都有一个额外的元数据:javax.management.Descriptor,它是用来设定 Model Bean 策略的。数据的存储是典型的 “key-value” 键值对。不同的 Model Bean 实现,以及不同的 MBeanFeatureInfo 支持不同的策略特性。下面我们就以 Attribute 为例,看一下 RequiredModelBean 支持的策略。

首先,它最重要的 Descriptor 主要是 name、displayName 和 descriptorType,其中 name 是属性名称。”name” 要与对应 ModelMBeanAttributeInfo 的 name 相同。descriptorType 必须是 “attribute”。

另外,value、default、legalValues “value” 是用来设定初始值的,”default” 指当不能从 resource 中获得该属性时的默认返回值,”legalValues” 是一组合法的属性数据。它并不用来保证 setAttribute 的数据一致性,而是在 UI 系统,如 JConsole 中提示用户可能的数据输入。

在属性访问的 getMethod, setMethod 方法上,事实上所有对属性的访问都会被 delegate 给同一 MBeanInfo 中特定的 Operation。 getMethod/setMethod 给出了对应的 ModelMBeanOperationInfo 名称。

还有一些额外的属性,比如:persistPolicy, persistPeriod 是代表了持久化策略;currencyTimeLimit, lastUpdatedTimeStamp 缓存策略;iterable 属性是否必须使用 iterate 来访问。默认为否;protocolMap 定义了与第三方系统有关的数据转换的 data model;visibility 定义了与第三方 UI 系统有关的 MBean 如何显示的策略;presentationString 也是定义了与第三方 UI 系统有关的 MBean 如何显示策略,比如 “presentation=server.gif”。

事实上,策略特性有两个层次的作用域:整个 Model Bean 和特定的 MBeanFeature。

Model Bean 的策略描述会被施加到该 Model Bean 的所有 MBeanFeature 上去,除非该 MBeanFeature 重写了这个策略特性。

在上面的例子里,这一个语句:

	 serverMMBeanInfo.setMBeanDescriptor(serverDescription);

给整个 serverMBeanInfo 设了一个策略描述 serverDescription,其中用 “currencyTimeLimit=10” 指出属性的缓存时间是 10 秒。所以,在 Main 方法中,两次 serverMBean.getAttribute(“upTime”);之间的间隔小于 10 秒就会得到同样的缓存值。

如果我们不想让 “upTime” 这个属性被缓存,我们可以在它的策略描述中加入 “currencyTimeLimit=-1”:

 Descriptor upTime =    new DescriptorSupport(
		        new String[] {
		          "name=upTime",
		          "descriptorType=attribute",
		          "displayName=Server upTime",
		          "getMethod=getUpTime",
		          "currencyTimeLimit=-1" // 不需要缓存
		           });

 Descriptor getUpTimeDesc =
		      new DescriptorSupport(
		        new String[] {
		          "name=getUpTime",
		          "descriptorType=operation",
		          "class=modelmbean.Server",
		          "role=operation"
		          ,"currencyTimeLimit=-1" // 不需要缓存
			  });

getUpTimeDesc 也要改动的原因是 RequiredModelBean 会把获取 upTime 属性的工作 delegate 给 getUpTime invocation。只要其中一处使用 MBean 级的缓存策略,就没法获得实时 upTime 数据了。

虚拟机检测

JMX 与虚拟机检测

JMX 的提出,为 Java 虚拟机提供了 Java 层上的检测机制。J2SE 中,新提出的 java.lang.management包即是 JMX 在 JDK 的一个应用,它提供了大量的有用的接口,通过 MBean 方式,提供了对 Java 虚拟机和运行时远端的监控和检测方式,来帮助用户来检测本地或者远端的虚拟机的运行情况。有了 JMX 之后,我们可以设计一个客户端,来检测远端一个正在运行的虚拟机中的线程数、线程当前的 Stack、内存管理、GC 所占用的时间、虚拟机中的对象和当前虚拟机参数等重要的参数和运行时信息。JMX 另外的一个重要功能是对配置信息的检测和再配置。比如,我们可以在远端查看和修改当前 JVM 的 verbose 参数,以达到动态管理的目的。甚至,我们可以在远端指挥 JVM 做一次 GC,这在下文中有详细介绍。

JMX 提供的虚拟机检测 API

检测虚拟机当前的状态总是 Java 开放人员所关心的,也正是因为如此,出现了大量的 profiler 工具来检测当前的虚拟机状态。从 Java SE 5 之后,在 JDK 中,我们有了一些 Java 的虚拟机检测 API,即 java.lang.management包。Management 包里面包括了许多 MXBean 的接口类和 LockInfo、MemoryUsage、MonitorInfo 和 ThreadInfo 等类。从名字可以看出,该包提供了虚拟机内存分配、垃圾收集(GC)情况、操作系统层、线程调度和共享锁,甚至编译情况的检测机制。这样一来,Java 的开发人员就可以很简单地为自己做一些轻量级的系统检测,来确定当前程序的各种状态,以便随时调整。

要获得这些信息,我们首先通过 java.lang.management.ManagementFactory这个工厂类来获得一系列的 MXBean。包括:

  • ClassLoadingMXBean

    ClassLoadMXBean 包括一些类的装载信息,比如有多少类已经装载 / 卸载(unloaded),虚拟机类装载的 verbose 选项(即命令行中的 Java – verbose:class 选项)是否打开,还可以帮助用户打开 / 关闭该选项。

  • CompilationMXBean

    CompilationMXBean 帮助用户了解当前的编译器和编译情况,该 mxbean 提供的信息不多。

  • GarbageCollectorMXBean

    相对于开放人员对 GC 的关注程度来说,该 mxbean 提供的信息十分有限,仅仅提供了 GC 的次数和 GC 花费总时间的近似值。但是这个包中还提供了三个的内存管理检测类:MemoryManagerMXBean,MemoryMXBean 和 MemoryPoolMXBean。

    • MemoryManagerMXBean

      这个类相对简单,提供了内存管理类和内存池(memory pool)的名字信息。

    • MemoryMXBean

      这个类提供了整个虚拟机中内存的使用情况,包括 Java 堆(heap)和非 Java 堆所占用的内存,提供当前等待 finalize 的对象数量,它甚至可以做 gc(实际上是调用 System.gc)。

    • MemoryPoolMXBean

      该信息提供了大量的信息。在 JVM 中,可能有几个内存池,因此有对应的内存池信息,因此,在工厂类中,getMemoryPoolMXBean() 得到是一个 MemoryPoolMXBean 的 list。每一个 MemoryPoolMXBean 都包含了该内存池的详细信息,如是否可用、当前已使用内存 / 最大使用内存值、以及设置最大内存值等等。

  • OperatingSystemMXBean

    该类提供的是操作系统的简单信息,如构架名称、当前 CPU 数、最近系统负载等。

  • RuntimeMXBean

    运行时信息包括当前虚拟机的名称、提供商、版本号,以及 classpath、bootclasspath 和系统参数等等。

  • ThreadMXBean

    在 Java 这个多线程的系统中,对线程的监控是相当重要的。ThreadMXBean 就是起到这个作用。ThreadMXBean 可以提供的信息包括各个线程的各种状态,CPU 占用情况,以及整个系统中的线程状况。从 ThreadMXBean 可以得到某一个线程的 ThreadInfo 对象。这个对象中则包含了这个线程的所有信息。

java.lang.management 和虚拟机的关系

我们知道,management 和底层虚拟机的关系是非常紧密的。其实,有一些的是直接依靠虚拟机提供的公开 API 实现的,比如 JVMTI;而另外一些则不然,很大一块都是由虚拟机底层提供某些不公开的 API / Native Code 提供的。这样的设计方式,保证了 management 包可以提供足够的信息,并且使这些信息的提供又有足够的效率;也使 management 包和底层的联系非常紧密。

Java 6 中的 API 改进

Management 在 Java SE 5 被提出之后,受到了欢迎。在 Java 6 当中,这个包提供更多的 API 来更好地提供信息。

OperatingSystemMXBean. getSystemLoadAverage()

Java 程序通常关注是虚拟机内部的负载、内存等状况,而不考虑整个系统的状况。但是很多情况下,Java 程序在运行过程中,整个计算机系统的系统负荷情况也会对虚拟机造成一定的影响。随着 Java 的发展,Java 程序已经覆盖了各个行业,这一点也必须得到关注。在以前,利用 Native 代码来检测系统负载往往是唯一的选择,但是在 Java 6 当中,JDK 自己提供了一个轻量级的系统负载检测 API,即OperatingSystemMXBean.getSystemLoadAverage()。

当然这个 API 事实上仅仅返回一个对前一分钟系统负载的简单的估测。它设计的主要目标是简单快速地估测当前系统负荷,因此它首先保证了这个 API 的效率是非常高的;也因为如此,这个 API 事实上并不适用于所有的系统。

锁检测

我们知道,同步是 Java 语言很重要的一个特性。在 Java SE 中,最主要的同步机制是依靠 synchronize 关键字对某一个对象加锁实现的;在 Java SE 5 之后的版本中,concurrent 包的加入,大大强化了 Java 语言的同步能力,concurrent 提供了很多不同类型的锁机制可供扩展。因此,要更好地观测当前的虚拟机状况和不同线程的运行态,去观察虚拟机中的各种锁,以及线程与锁的关系是非常必要的。很可惜的是,在过去的 JDK 中,我们并没有非常方便的 API 以供使用。一个比较直接的检测方式是查看线程的 stack trace,更为强大全面(但是也更复杂并且效率低下)的方案是得到一个 VM 所有对象的快照并查找之,这些策略的代价都比较大,而且往往需要编写复杂的 Native 代码。

JDK 6 里提供了一些相当简单的 API 来提供这个服务。首先了解两个新类,LockInfo 和 MonitorInfo 这两个类承载了锁的信息。LockInfo 可以是任何的 Java 锁,包括简单 Java 锁和 java.util.concurrent包中所使用的锁(包括 AbstractOwnableSynchronizer 和 Condition 的实现类 / 子类),而 MonitorInfo 是简单的 Java 对象所代表的锁。要检测一个线程所拥有的锁和等待的锁,首先,要得到一个线程的 ThreadInfo,然后可以简单地调用:

  • getLockedMonitors()

    返回一个所有当前线程已经掌握的锁对象的列表。

  • getLockedSynchronizers()

    对于使用 concurrent 包的线程,返回一个该线程所掌握的“ownable synchronizer”(即 AbstractOwnableSynchronizer 及其子类)所组成的列表。

  • getLockInfo()

    当前线程正在等待的那个锁对象的信息就可以知道线程所有的锁信息。通过这些锁信息,我们很方便的可以知道当前虚拟机的所有线程的锁信息。由此,我们还可以推导出更多的信息。

死锁检测

死锁检测一直以来是软件工程师所重视的,显然一个死锁的系统永远是工程师最大的梦魇。Java 程序的死锁检测也一直以来是 Java 程序员所头痛的。为了解决线程间死锁问题,一般都有预防(代码实现阶段)和死锁后恢复(运行时)两种方式。以前 Java 程序员都重视前者,因为在运行态再来检测和恢复系统是相当麻烦的,缺少许多必要的信息;但是,对于一些比较复杂的系统,采取后者或者运行时调试死锁信息也是非常重要的。由上面所说,现在我们已经可以知道每一个线程所拥有和等待的锁,因此要计算出当前系统中是否有死锁的线程也是可行的了。当然,Java 6 里面也提供了一个 API 来完成这个功能,即:

  • ThreadMXBean.findDeadlockedThreads()

    这个函数的功能就是检测出当前系统中已经死锁的线程。当然,这个功能复杂,因此比较费时。基本上仅仅将之用于调试,以便对复杂系统线程调用的改进。

未来的发展

JMX 在 Java SE 5/6 中的功能已经相当强大,但是距离 Java 程序开发人员的要求还是有一段距离,因此 Sun 公司已经向 JCP 提出了 JSR 255 (JMX API 2.0 版本)来扩充和进一步发展 JMX,并希望这个 JSR 将在 Java SE 7 中实现。在这个文档中,新的 JMX 2.0 将着重于:

  • 对 management 模型的优化,并提供更好的支持;加入了比如 annotation 等等的新特性;
  • 对 JMX 定义的优化,在进一步强化 MBean 扩充性好的优点的同时,尽量改变(用户普遍认为的)MBean 很难实现的缺点;
  • 对非 Java 平台客户端的支持。这将是一个令人振奋的新特性;

具体的扩展可能包括:

  • 层次性的命名域(Hierarchical namespace);
  • 新的事件服务功能;
  • 对 locales 的新支持;
  • 为 MBean 启用 annotation 服务;
  • 也可以使用用户类型的 mapping 了;

可以看到,JMX 的进一步发展主要关注的是可扩展性、动态性和易用性等 Java 用户非常关注的方面。

转自:http://www.ibm.com/developerworks/cn/java/j-lo-jse63/

 Posted by at 下午6:45  Tagged with:
4月 242014
 

清晰易懂的一个实例,推荐:

JMX        即 Java Management Extensions   Java管理扩展
MBean   即 managed beans                         被管理的Beans
一个MBean是一个被管理的Java对象,有点类似于JavaBean,一个设备、一个应用或者任何资源都可以被表示为MBean,MBean会暴露一个接口对外,这个接口可以读取或者写入一些对象中的属性,通常一个MBean需要定义一个接口,以MBean结尾, 例如: EchoMBean, 格式为XXXMBean,这个是规范,必须得遵守。例如:

 

 

Java代码  收藏代码

  1. package com.haitao.jmx;  
  2.   
  3. /** 
  4.  * JMX MBean Interface 
  5.  *  
  6.  * @author haitao.tu 
  7.  *  
  8.  */  
  9. public interface EchoMBean {  
  10.   
  11.     public void print(String yourName);  
  12.   
  13. }  

 

 

很简单的,在EchoMBean接口中,定义了一个print方法,带一个yourName的String类型参数,只有接口貌似没什么用,下边我们实现这个接口

 

 

Java代码  收藏代码

  1. package com.haitao.jmx;  
  2.   
  3. /** 
  4.  * Implements of JMX EchoMBean 
  5.  *  
  6.  * @author haitao.tu 
  7.  * 
  8.  */  
  9. public class Echo implements EchoMBean {  
  10.   
  11.     @Override  
  12.     public void print(String yourName) {  
  13.         System.out.println(“Hi “ + yourName + “!”);  
  14.     }  
  15.       
  16. }  

 


Echo实现了EchoMBean接口,很简单我们只是print了hi yourName!
按照JMX的定义,是被管理的对象,现在我们只是定义了该对象,并没有被管理,接着我们让这个Echo类的实例对象被管理起来:

Java代码  收藏代码

  1. package com.haitao.jmx;  
  2.   
  3. import java.lang.management.ManagementFactory;  
  4.   
  5. import javax.management.MBeanServer;  
  6. import javax.management.ObjectName;  
  7.   
  8. /** 
  9.  * JMX App Demo 
  10.  *  
  11.  * @author haitao.tu 
  12.  */  
  13. public class App {  
  14.       
  15.     public static void main(String[] args) throws Exception {  
  16.         // 创建MBeanServer  
  17.         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();  
  18.           
  19.         // 新建MBean ObjectName, 在MBeanServer里标识注册的MBean  
  20.         ObjectName name = new ObjectName(“com.haitao.jmx:type=Echo”);  
  21.           
  22.         // 创建MBean  
  23.         Echo mbean = new Echo();  
  24.           
  25.         // 在MBeanServer里注册MBean, 标识为ObjectName(com.tenpay.jmx:type=Echo)  
  26.         mbs.registerMBean(mbean, name);  
  27.   
  28.           
  29.         // 在MBeanServer里调用已注册的EchoMBean的print方法  
  30.         mbs.invoke(name, “print”new Object[] { “haitao.tu”}, new String[] {“java.lang.String”});  
  31.           
  32.         Thread.sleep(Long.MAX_VALUE);  
  33.     }  
  34.   
  35. }  

 


1. 首先我们在App类中向ManagementFactory申请了一个MBeanServer对象
2. 接着我们即然要使Echo的实例对象被管理起来,我们就需要给这个对象一个标识,这个标识是ObjectName.注意这个ObjectName构造函数,这里使用了(包名:type=类名)的形式.
3. 然后我们通过mbs.registerMBean方法注册了echo,并传入了ObjectName在MBeanServer中标识该MBean.
4. 随后我们通过mbs.invoke方法调用了已经注册的Echo的print方法,通过ObjectName找到该MBean, 并通过最后两个参数,传入print方法执行的参数,与参数的类型。
5. 最后我们sleep主线程,等待其他线程的调用.
通过这个例子我们可以看出,MBean的好处,在Echo的实例对象未被管理之前,我们只能通过Echo对象的句柄,来调用Echo里的public方法,在被管理之后,我们可以通过MBeanServer的句柄mbs来调用Echo对象的print方法。
更详细的内容,我们可以通过JDK自带工具jconsole或者 VisualVM 来查看MBean:

 

1.jconsole

 

jconsole的位置在%JAVA_HOME%binjconsole.exe

 

开后会看到:

jconsole

 

 

选择com.haitao.jmx.App,然后点击连接, 选择MBean后会看到

jconsole-MBean

 

这里可以直接调用Echo的print方法, 当然大家看到这里边还有好多其他的table,包括概述、内存、线程、类、VM摘要、MBean。

 

 

2.VisualVM

 

VisualVM的位置在%JAVA_HOME%binjvisualvm.exe

 

打开后经过连接我们会看到类似的效果:

 

 

VisualVM

 转自:http://tuhaitao.iteye.com/blog/786391

 Posted by at 下午5:59  Tagged with:
4月 202014
 

接着上一篇继续推荐,来自:http://blog.csdn.net/derekjiang/article/details/4532375

上一篇我们一起来研究了JMX的Server端实现,现在我们再来一起看看怎样来写客户端来连接Server。

 

客户端案例:

 

  1. package study.test.jmx;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.Set;  
  5.   
  6. import javax.management.Attribute;  
  7. import javax.management.MBeanServerConnection;  
  8. import javax.management.MBeanServerInvocationHandler;  
  9. import javax.management.ObjectName;  
  10. import javax.management.remote.JMXConnector;  
  11. import javax.management.remote.JMXConnectorFactory;  
  12. import javax.management.remote.JMXServiceURL;  
  13.   
  14. public class HelloWorldClient {  
  15.   
  16.     /** 
  17.      * @param args 
  18.      */  
  19.     public static void main(String[] args) {  
  20.         try {  
  21.             // Create an RMI connector client and  
  22.             // connect it to the RMI connector server  
  23.             //  
  24.             echo(“/nCreate an RMI connector client and ” + “connect it to the RMI connector server”);  
  25.             JMXServiceURL url = new JMXServiceURL(“service:jmx:rmi:///jndi/rmi://localhost:1099/TestJMXServer”);  
  26.             JMXConnector jmxc = JMXConnectorFactory.connect(url, null);  
  27.       
  28.             // Get an MBeanServerConnection  
  29.             //  
  30.             echo(“/nGet an MBeanServerConnection”);  
  31.             MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();  
  32.       
  33.             // Get domains from MBeanServer  
  34.             //  
  35.             echo(“/nDomains:”);  
  36.             String domains[] = mbsc.getDomains();  
  37.             for (int i = 0; i < domains.length; i++) {  
  38.                 echo(“/tDomain[” + i + “] = ” + domains[i]);  
  39.             }  
  40.       
  41.             // Get MBean count  
  42.             //  
  43.             echo(“/nMBean count = ” + mbsc.getMBeanCount());  
  44.       
  45.             // Query MBean names  
  46.             //  
  47.             echo(“/nQuery MBeanServer MBeans:”);  
  48.             Set names = mbsc.queryNames(null, null);  
  49.             for (Iterator i = names.iterator(); i.hasNext(); ) {  
  50.                 echo(“/tObjectName = ” + (ObjectName) i.next());  
  51.             }  
  52.               
  53.       
  54.             // get MBean obj name  
  55.             //  
  56.             ObjectName stdMBeanName = new ObjectName(“TestJMXServer:name=HelloWorld”);  
  57.               
  58.             // Access MBean   
  59.             // Get attribute in MBean  
  60.             echo(“/nName = ” + mbsc.getAttribute(stdMBeanName, “Name”));  
  61.             // set attribute value in MBean  
  62.             mbsc.setAttribute(stdMBeanName, new Attribute(“Name”,”小三黑”));  
  63.             // invoke the MBean’s method  
  64.             mbsc.invoke(stdMBeanName, “printHello”, null, null);          
  65.             mbsc.invoke(stdMBeanName, “printHello”, new Object[] { “HelloKity” }, new String[] { String.class.getName() });  
  66.               
  67.             // Proxy way to access MBean  
  68.             HelloWorldMBean proxy = (HelloWorldMBean) MBeanServerInvocationHandler.newProxyInstance(mbsc, stdMBeanName, HelloWorldMBean.class, false);  
  69.             echo(proxy.getName());  
  70.             proxy.setName(“帅哥一个”);     
  71.             echo(proxy.getName());  
  72.             proxy.printHello();  
  73.                        
  74.               
  75.             // Close MBeanServer connection  
  76.             //  
  77.             echo(“/nClose the connection to the server”);  
  78.             jmxc.close();  
  79.             echo(“/nBye! Bye!”);  
  80.         } catch (Exception e) {  
  81.             e.printStackTrace();  
  82.         }  
  83.     }  
  84.   
  85.     private static void echo(String msg) {  
  86.         System.out.println(msg);  
  87.     }  
  88.   
  89.   
  90. }  

 

程序里面的注释写的比较明白,这里就不再多说了,让我们一起来看看程序运行的结果:

 

[c-sharp] 

  1. Create an RMI connector client and connect it to the RMI connector server  
  2.   
  3. Get an MBeanServerConnection  
  4.   
  5. Domains:  
  6.     Domain[0] = TestJMXServer  
  7.     Domain[1] = JMImplementation  
  8.   
  9. MBean count = 3  
  10.   
  11. Query MBeanServer MBeans:  
  12.     ObjectName = JMImplementation:type=MBeanServerDelegate  
  13.     ObjectName = TestJMXServer:name=HelloWorld  
  14.     ObjectName = TestJMXServer:name=htmladapter  
  15.   
  16. Name = null  
  17. 小三黑  
  18. 帅哥一个  
  19.   
  20. Close the connection to the server  
  21.   
  22. Bye! Bye!  

 

服务器端的运行结果如下:

 

[java] view plaincopy

  1. JMXServiceURL: service:jmx:rmi:///jndi/rmi://localhost:1099/TestJMXServer  
  2. Hello World, 小三黑  
  3. Hello , HelloKity  
 Posted by at 下午11:35  Tagged with:
4月 202014
 

非常不错的一篇JMX入门文章,推荐。

最近在项目中用到了JMX,以前只是简略的知道JMX是干什么的,却没有深入了解过,当然也没有写过相关的代码,借着这个机会,正好学习一下。Google了n多大侠的相关帖子,对JMX有了一个初步的理解,这里总结出来,希望对大家有用。

 

关于什么是JMX,这里引用了网上找来的一个介绍:JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理

 

老实说,看了这个介绍,我还是不太明白,用我们都能理解的大白话来说,JMX是一个框架,它提供了这样一种功能:通过使用JMX,我们可以实时查询应用程序中通过JMX向外公布的相应参数或者是其他应用数据,同时,我们也可以通过JMX来实时的调用应用程序使用JMX向外公布的接口,从而来实现一些操作。

 

准备工作


JMX是一份规范,SUN依据这个规范在JDK(1.3、1.4、5.0)提供了JMX接口。而根据这个接口的实现则有很多种,比如Weblogic的JMX实现、MX4J、JBoss的JMX实现。在SUN自己也实现了一份,不过在JDK1.4之前,这件JMX实现(一些JAR包)是可选的,你得去它的网站上下载。JDK5.0则内嵌了进来,安装JDK5.0就可以开发基于JMX的代码了。

但JDK5.0并非包含所有SUN的关于JMX的代码,有一些工具类是排除在JDK5.0之外的。下面根据所使用的JDK版本情况,谈一谈开发环境的准备。
1、JDK1.3、1.4
去SUN网站下载SUN的JMX实现,共两个ZIP文件,下载网址:http://java.sun.com/products/JavaManagement/download.html
(1)jmx-1_2_1-ri.zip
解压后的lib目录包含:jmxri.jar、jmxtools.jar
(2)jmx_remote-1_0_1_03-ri.zip
解压后的lib目录包含:jmxremote.jar、jmxremote_optional.jar、rmissl.jar
如果在DOS下用命令行开发,则把这五个JAR包加入到classpath系统变量中。如果你用Eclipse开发,则把JAR包加入到项目属性的Libratries(库)引用中。
2、JDK5.0以上
JDK5.0的jre/lib/rt.jar已经包含了jmxri.jar、jmxremote.jar、rmissl.jar三个包的代码。如果你用到jmxtools.jar、jmxremote_optional.jar的类,则需要将这两个类加入到classpath或Eclipse的项目库引用中。
3、我使用的开发环境:JDK5.0 + Eclipse3.2。
注:因为用到jmxtools.jar中的HtmlAdaptorServer类,所以将此包加入到项目库引用中。
Server端程序范例:
1. MBean接口
这是一个应用程序将要向外暴露的接口,在该接口中需要定义要公布的所有函数。
其中,如果存在一对对应的get方法和set方法,那么就默认有一个属性,且熟悉的名字就是get方法名字中get后面的部分。
包含在MBean中方法都将是可以被管理的。MBean起名是有规范的,就是原类名后加上MBean字样。
[java] view plaincopy

  1. package study.test.jmx;  
  2.   
  3. public interface HelloWorldMBean {  
  4.     public String getName();          
  5.     public void setName(String name);      
  6.     public void printHello();         
  7.     public void printHello(String whoName);    
  8. }  

2. 被管理的类
被管理的类需要实现相应的MBean接口,通过MBean接口中的方法来被管理。
[java] view plaincopy

  1. package study.test.jmx;  
  2.   
  3. public class HelloWorld implements HelloWorldMBean {  
  4.       
  5.     private String name;      
  6.       
  7.     public String getName() {      
  8.         return name;      
  9.     }      
  10.      
  11.     public void setName(String name) {      
  12.         this.name = name;      
  13.     }      
  14.      
  15.     public void printHello() {      
  16.         System.out.println(“Hello World, “ + name);      
  17.     }      
  18.      
  19.     public void printHello(String whoName) {      
  20.         System.out.println(“Hello , “ + whoName);      
  21.     }      
  22.   
  23. }  

3. 创建一个Agent类
Agent其实实现的是类似于Server的功能,他负责把JMX服务绑定到相应的URL,并将我们上面创建的被管理的类绑定到其中,使得外部可以访问。
[java] view plaincopy

  1. package study.test.jmx;  
  2.   
  3. import java.io.IOException;  
  4. import java.rmi.registry.LocateRegistry;  
  5. import java.rmi.registry.Registry;  
  6.   
  7. import javax.management.InstanceAlreadyExistsException;  
  8. import javax.management.MBeanRegistrationException;  
  9. import javax.management.MBeanServer;  
  10. import javax.management.MBeanServerFactory;  
  11. import javax.management.MalformedObjectNameException;  
  12. import javax.management.NotCompliantMBeanException;  
  13. import javax.management.ObjectName;  
  14. import javax.management.remote.JMXConnectorServer;  
  15. import javax.management.remote.JMXConnectorServerFactory;  
  16. import javax.management.remote.JMXServiceURL;  
  17.   
  18. import com.sun.jdmk.comm.HtmlAdaptorServer;  
  19.   
  20. public class HelloWorldAgent {  
  21.   
  22.     public static void main(String[] args) throws MalformedObjectNameException,  
  23.             NullPointerException, InstanceAlreadyExistsException,  
  24.             MBeanRegistrationException, NotCompliantMBeanException, IOException {  
  25.   
  26.         int rmiPort = 1099;  
  27.         String jmxServerName = “TestJMXServer”;  
  28.           
  29.         // jdkfolder/bin/rmiregistry.exe 9999         
  30.         Registry registry = LocateRegistry.createRegistry(rmiPort);  
  31.   
  32.         MBeanServer mbs = MBeanServerFactory.createMBeanServer(jmxServerName);  
  33.         //MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();  
  34.       
  35.         HtmlAdaptorServer adapter = new HtmlAdaptorServer();  
  36.         ObjectName adapterName;  
  37.         adapterName = new ObjectName(jmxServerName + “:name=” + “htmladapter”);  
  38.         adapter.setPort(8082);  
  39.         adapter.start();  
  40.         mbs.registerMBean(adapter, adapterName);  
  41.   
  42.         ObjectName objName = new ObjectName(jmxServerName + “:name=” + “HelloWorld”);  
  43.         mbs.registerMBean(new HelloWorld(), objName);  
  44.           
  45.   
  46.         JMXServiceURL url = new JMXServiceURL(“service:jmx:rmi:///jndi/rmi://localhost:” + rmiPort + “/” + jmxServerName);  
  47.         System.out.println(“JMXServiceURL: “ + url.toString());  
  48.         JMXConnectorServer jmxConnServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);  
  49.         jmxConnServer.start();  
  50.   
  51.     }  
  52. }  

说明:
1. Agent实现中的35行到40行为MBeanServer添加了一个htmladapter,这样我们就可以通过网页的方式来进行管理。
 比如说上面我们实行的Agent,我们就可以通过http://localhost:8082来对程序进行管理。这里的8082就是htmladapter中设置的端口。
通过我们设定的MBean名字点击去,就可以进一步来管理我们的MBean

 从这张图我们可以看出,我们可以直接从网页上面来改动Name的值,并且也可以直接通过点击来调用相应的方法:printHello
 
 
 2. 如果大家在尝试写Agent程序时出现connection refused的异常的时候,不用怕,赶紧检查一下你的程序中是不是有这句话:
   Registry registry = LocateRegistry.createRegistry(rmiPort);
   LocateRegistry.createRegistry(int port)方法可以在某一特定端口创建名字服务,从而用户无需再手工启动rmiregistry
   或者,你也可以运行  jdkfolder/bin/rmiregistry.exe 9999
   其中jdkfolder是你的jdk的安装目录,9999是你要绑定的端口
   运行上面的命令和你在代码中添加上面那行code是一样的效果
   
 3. Agent的实现中的32行和33行是两种获取MBeanServer的方式。
    MBeanServer mbs = MBeanServerFactory.createMBeanServer(jmxServerName);
    这种方式主要用于JDK1.5以前
  MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
   这种方式是JDK1.5引入的。
   安全起见,当大家不能确定以后部署的机器上面安装的JDK是1.5以上的版本时,建议按照第一种方式。
   
 4. 绑定需要被管理的类
    ObjectName objName = new ObjectName(jmxServerName + “:name=” + “HelloWorld”);
  mbs.registerMBean(new HelloWorld(), objName);
  这里我们可以看到,首先要给被绑定的类起一个名字,然后把这个名字和被管理的类一起注册到MBeanServer当中。
  
5. 将服务绑定到固定的URL上
  JMXServiceURL url = new JMXServiceURL(“service:jmx:rmi:///jndi/rmi://localhost:” + rmiPort + “/” + jmxServerName);
  JMXConnectorServer jmxConnServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
  jmxConnServer.start();

  在start方法被调用后,服务就已经发布成功,我们就可以通过页面或者其他的方式来访问服务


备注:

关于HtmlAdaptorServer类找不到解决方法,需要jmxtools.jar文件,下载地址: 

http://java.sun.com/javase/technologies/core/mntr-mgmt/javamanagement/download.jsp 
=> JMX 1.2.1 Reference Implementation 


转自:http://blog.csdn.net/derekjiang/article/details/4531952

 Posted by at 下午11:31  Tagged with: