技术开发 频道

Java SE 6 新特性: JMX 与系统管理

        动态 MBean

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

1 package dynamicbeans;
2
3 import javax.management.*;
4 import java.lang.reflect.*;
5 public class ServerMonitor implements DynamicMBean {
6
7     private final ServerImpl target;    
8     private MBeanInfo mBeanInfo;    
9         
10     public ServerMonitor(ServerImpl target){
11         this.target = target;
12     }
13     
14     //实现获取被管理的 ServerImpl 的 upTime
15     public long upTime(){
16         return System.currentTimeMillis() - target.startTime;
17     }
18
19     //javax.management.MBeanServer 会通过查询 getAttribute("Uptime") 获得 "Uptime" 属性值
20     public Object getAttribute(String attribute) throws AttributeNotFoundException,
21         MBeanException, ReflectionException {
22         if(attribute.equals("UpTime")){
23             return upTime();
24         }
25         return null;
26     }
27     
28     //给出 ServerMonitor 的元信息。  
29     public MBeanInfo getMBeanInfo() {
30         if (mBeanInfo == null) {
31             try {
32                 Class cls = this.getClass();
33                 //用反射获得 "upTime" 属性的读方法
34                 Method readMethod = cls.getMethod("upTime", new Class[0]);
35                 //用反射获得构造方法
36                 Constructor constructor = cls.getConstructor(new Class[]
37                     {ServerImpl.class});
38                 //关于 "upTime" 属性的元信息:名称为 UpTime,只读属性(没有写方法)。
39                 MBeanAttributeInfo upTimeMBeanAttributeInfo = new MBeanAttributeInfo(
40                         "UpTime", "The time span since server start",
41                         readMethod, null);
42                 //关于构造函数的元信息
43                 MBeanConstructorInfo mBeanConstructorInfo = new MBeanConstructorInfo(
44                         "Constructor for ServerMonitor", constructor);
45                 //ServerMonitor 的元信息,为了简单起见,在这个例子里,
46                 //没有提供 invocation 以及 listener 方面的元信息
47                 mBeanInfo = new MBeanInfo(cls.getName(),
48                         "Monitor that controls the server",
49                         new MBeanAttributeInfo[] { upTimeMBeanAttributeInfo },
50                         new MBeanConstructorInfo[] { mBeanConstructorInfo },
51                         null, null);                
52             } catch (Exception e) {
53                 throw new Error(e);
54             }
55
56         }
57         return mBeanInfo;
58     }
59
60     public AttributeList getAttributes(String[] arg0) {        
61         return null;
62     }
63         
64     public Object invoke(String arg0, Object[] arg1, String[] arg2)
65         throws MBeanException,
66         ReflectionException {        
67         return null;
68     }
69
70     public void setAttribute(Attribute arg0) throws AttributeNotFoundException,
71         InvalidAttributeValueException, MBeanException, ReflectionException {
72         return;        
73     }
74
75     public AttributeList setAttributes(AttributeList arg0) {        
76         return null;
77     }  
78 }
79

  其它动态 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 端:

1 package modelmbean;
2
3 public class Server {
4
5     private long startTime;
6     
7     public Server() {    }
8     
9     public int start(){
10         startTime = System.currentTimeMillis();
11         return 0;
12     }
13     
14     public long getUpTime(){
15         return System.currentTimeMillis() - startTime;
16     }
17 }
18

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

  1 package modelmbean;
  2
  3 import javax.management.*;
  4 import javax.management.modelmbean.*;
  5 public class Main {
  6     
  7     public static void main(String[] args) throws Exception{
  8         MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer();
  9         RequiredModelMBean serverMBean =
10             (RequiredModelMBean) mBeanServer.instantiate(
11               "javax.management.modelmbean.RequiredModelMBean");        
12         
13         ObjectName serverMBeanName =
14             new ObjectName("server: id=Server");
15         serverMBean.setModelMBeanInfo(getModelMBeanInfoForServer(serverMBeanName));        
16 |-------10--------20--------30--------40--------50--------60--------70--------80--------9|
17 |-------- XML error:  The previous line is longer than the max of 90 characters ---------|
18         Server server = new Server();
19         serverMBean.setManagedResource(server, "ObjectReference");
20         
21         ObjectInstance registeredServerMBean =
22             mBeanServer.registerMBean((Object) serverMBean, serverMBeanName);
23         
24         serverMBean.invoke("start",null, null);
25         
26         Thread.sleep(1000);
27         
28         System.out.println(serverMBean.getAttribute("upTime"));
29         Thread.sleep(5000);
30         System.out.println(serverMBean.getAttribute("upTime"));
31     }
32     
33     private static ModelMBeanInfo getModelMBeanInfoForServer(ObjectName objectName)
34         throws Exception{
35         ModelMBeanAttributeInfo[] serverAttributes =
36               new ModelMBeanAttributeInfo[1];
37         Descriptor upTime =
38               new DescriptorSupport(
39                 new String[] {
40                   "name=upTime",
41                   "descriptorType=attribute",
42                   "displayName=Server upTime",
43                   "getMethod=getUpTime",                  
44                    });
45         serverAttributes[0] =
46               new ModelMBeanAttributeInfo(
47                 "upTime",
48                 "long",
49                 "Server upTime",
50                 true,
51                 false,
52                 false,
53                 upTime);
54         
55         ModelMBeanOperationInfo[] serverOperations =
56               new ModelMBeanOperationInfo[2];
57         
58         Descriptor getUpTimeDesc =
59               new DescriptorSupport(
60                 new String[] {
61                   "name=getUpTime",
62                   "descriptorType=operation",
63                   "class=modelmbean.Server",
64                   "role=operation"                  
65                   });
66         
67         MBeanParameterInfo[] getUpTimeParms = new MBeanParameterInfo[0];
68         serverOperations[0] = new ModelMBeanOperationInfo("getUpTime",
69                   "get the up time of the server",
70                   getUpTimeParms,
71                   "java.lang.Long",
72                   MBeanOperationInfo.ACTION,
73                   getUpTimeDesc);
74             
75         Descriptor startDesc =
76               new DescriptorSupport(
77                 new String[] {
78                   "name=start",
79                   "descriptorType=operation",
80                   "class=modelmbean.Server",
81                   "role=operation"
82                   });
83         MBeanParameterInfo[] startParms = new MBeanParameterInfo[0];
84         serverOperations[1] = new ModelMBeanOperationInfo("start",
85                   "start(): start server",
86                   startParms,
87                   "java.lang.Integer",
88                   MBeanOperationInfo.ACTION,
89                   startDesc);
90         
91         ModelMBeanInfo serverMMBeanInfo =
92               new ModelMBeanInfoSupport(
93                 "modelmbean.Server",
94                 "ModelMBean for managing an Server",
95                 serverAttributes,
96                 null,
97                 serverOperations,
98                 null);
99         
100         //Default strategy for the MBean.
101         Descriptor serverDescription =
102               new DescriptorSupport(
103                 new String[] {
104                   ("name=" + objectName),
105                   "descriptorType=mbean",
106                   ("displayName=Server"),
107                   "type=modelmbean.Server",
108                   "log=T",
109                   "logFile=serverMX.log",
110                   "currencyTimeLimit=10" });
111         serverMMBeanInfo.setMBeanDescriptor(serverDescription);
112        return serverMMBeanInfo;
113       }
114

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

  创建一个 MBServer:mBeanServe

  获得管理资源用的 MBean:serverBean

  给这个 MBean 一个 ObjectName:serverMBeanName

  将 serverBean 以 serverMBeanName 注册到 mBeanServer 上去

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

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

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

  类似于普通的动态 MBean,需要 MBean 的 Attribute、Invocation、Notification 的类型/反射信息,诸如返回类型、参数类型和相关的 get/set 方法等。这里将不再赘述。

  关于缓存、持久化以及日志等的策略。后面我们将介绍一些这方面的信息。

  第二步指出了 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 重写了这个策略特性。

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

1 serverMMBeanInfo.setMBeanDescriptor(serverDescription);

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

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

1 Descriptor upTime =    new DescriptorSupport(
2                 new String[] {
3                   "name=upTime",
4                   "descriptorType=attribute",
5                   "displayName=Server upTime",
6                   "getMethod=getUpTime",
7                   "currencyTimeLimit=-1" //不需要缓存
8                    });
9
10 Descriptor getUpTimeDesc =
11               new DescriptorSupport(
12                 new String[] {
13                   "name=getUpTime",
14                   "descriptorType=operation",
15                   "class=modelmbean.Server",
16                   "role=operation"
17                   ,"currencyTimeLimit=-1" //不需要缓存
18               });
19

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

0
相关文章