5.2 创建一个客户化的JMX客户端

2016-01-13 22:22:26 6,687 1

本教程的前几堂课程已经想你展示了如何创建 JMX MBeans和MXBeans以及如何在JMX Agent中注册它们。然而,所有前面的例子使用的都是现有的JMX 客户端 - JConsole。这堂课将展示如何创建我们自己的客户化的JMX客户端。

一个客户化的JMX客户端,Client,包含在jmx_examples.zip中。该JMX客户端和前面课程中介绍的的同样的MBean, MXBean以及JMX agent进行交互。由于Client比较大,我们将在下面章节中逐块地查看它。

导入JMX Remote API类

要想创建从JMX Client到远程JMX Agents的连接,你需要使用来自 javax.management.remote 这个包的类。

package com.example;
...

import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class Client {
...

Client类需要一个JMXConnectorFactory和一个JMXServiceURL来创建 JMXConnector 实例。

Creating a Notification Listener

JMX client需要一个通知处理器,用于监听并处理由注册在JMX agent的MBean Server中的MBeans发出的任何通知。JMX client的通知处理器是NotificationListener接口的一个实例,如下所示:

... 

public static class ClientListener implements NotificationListener {

 public void handleNotification(Notification notification,
 Object handback) {
 echo("\nReceived notification:");
 echo("\tClassName: " + notification.getClass().getName());
 echo("\tSource: " + notification.getSource());
 echo("\tType: " + notification.getType());
 echo("\tMessage: " + notification.getMessage());
 if (notification instanceof AttributeChangeNotification) {
 AttributeChangeNotification acn =
 (AttributeChangeNotification) notification;
 echo("\tAttributeName: " + acn.getAttributeName());
 echo("\tAttributeType: " + acn.getAttributeType());
 echo("\tNewValue: " + acn.getNewValue());
 echo("\tOldValue: " + acn.getOldValue());
 }
 }
} 
...

这个通知监听器判定它所收到的通知的来源,提取存储在该通知中的信息。然后根据收到的通知的类型,对通知信息做不同的处理。在这里,当监听器收到 AttributeChangeNotification 类型的通知时,它将通过调用AttributeChangeNotificationgetAttributeName、getAttributeTypegetNewValue和getOldValue方法,获取 已经被改变的MBean属性的名字和类型,以及新值和旧值。

然后创建一个新的 ClientListener 实例。

ClientListener listener = new ClientListener();

创建一个RMI Connector Client

这个Client类创建了一个RMI connector client,我们对它进行配置以便连接到RMI connector server上,当你启动JMX Agent Main 的时候就会启动该RMI connector server。这样做使得这个JMX客户端能够和JMX agent进行交互,就仿佛两者运行在相同的机器上一样。

...
 
public static void main(String[] args) throws Exception {

echo("\nCreate an RMI connector client and " +
 "connect it to the RMI connector server");
JMXServiceURL url = 
 new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
 JMXConnector jmxc = JMXConnectorFactory.connect(url, null);//只是连接到RMI Registry!还没用连接到MBean Server。...

如你所见,Client定义了一个JMXServiceURL,命名为url,它代表着 connector client 期望找到的connector server的位置。该URL让connector client能够从 运行在本地主机的9999端口上的RMI注册机 获取 RMI connector server的stub,命名为jmxrmi,然后连接到RMI connector server。【提示: 如果connector client和connector server处于不同的机器上,那么需要用new JMXServiceURL("service:jmx:rmi:///jndi/rmi://${CONNECTOR_SERVER_IP}:9999 /jmxrmi")。MBean Server如果用RMI Connector,那么应该可以用"java.rmi.server.hostname"来设置RMI注册机的IP地址,参见:Start RMI Server using Specific IP Address

这样定义RMI注册机之后,就能够创建connector client了。connector client,命名为jmxc,是JMXConnector接口的一个实例,通过JMXConnectorFactoryconnect()方法创建。该connect()方法在创建的时候,被传入了参数 url 以及一个空的环境map。

连接到远程MBean Server

RMI connection就位之后,JMX client必须连接到远程MBean server,这样它才能够 通过远程JMX agent 和注册在该远程MBean server中的各个MBeans进行交互。

...
 
MBeanServerConnection mbsc = 
 jmxc.getMBeanServerConnection();
 
...

通过调用JMXConnector实例jmxc的getMBeanServerConnection()方法,就能够创建 MBeanServerConnection 的一个实例,命名为mbsc。

connector client现在已经连接到由JMX agent创建的MBean server上了,这样,它就能够注册MBeans并对MBeans执行操作了,该连接对于两端来说,都是完全透明。

首先,客户端定义一些简单的操作,以便发现注册在该agent的MBean Server之中的MBeans的信息。

...
 
echo("\nDomains:");
String domains[] = mbsc.getDomains();
Arrays.sort(domains);
for (String domain : domains) {//打印所有domains名字
 echo("\tDomain = " + domain);
}
 
...
 
echo("\nMBeanServer default domain = " + mbsc.getDefaultDomain());

echo("\nMBean count = " + mbsc.getMBeanCount());
echo("\nQuery MBeanServer MBeans:");
Set<ObjectName> names = 
 new TreeSet<ObjectName>(mbsc.queryNames(null, null));
for (ObjectName name : names) {
 echo("\tObjectName = " + name);
}
 
...

该客户端调用 MBeanServerConnection 的各种方法,以便获取不同MBeans所属的domains、注册在MBean Server中的MBeans的数量 以及 该客户端所发现的各个MBeans的对象名。

       


通过代理(Proxies)对远程MBeans执行操作

客户端创建注册在MBean Server中的Hello MBean的一个proxy,通过MBean server connection来访问该MBean。该MBean proxy位于客户端,它模拟了远程MBean。

...

ObjectName mbeanName = new ObjectName("com.example:type=Hello");HelloMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, 
 HelloMBean.class, true);//true:通知MBean Server: 该client proxy需要接收通知。echo("\nAdd notification listener...");
 mbsc.addNotificationListener(mbeanName, listener, null, null);
 echo("\nCacheSize = " + mbeanProxy.getCacheSize());mbeanProxy.setCacheSize(150);echo("\nWaiting for notification...");
sleep(2000);echo("\nCacheSize = " + mbeanProxy.getCacheSize());echo("\nInvoke sayHello() in Hello MBean...");
mbeanProxy.sayHello();echo("\nInvoke add(2, 3) in Hello MBean...");
echo("\nadd(2, 3) = " + mbeanProxy.add(2, 3));

waitForEnterPressed();
 
...

MBean proxies使得你能够通过一个Java接口来访问一个MBean,让你能够对该proxy进行调用,而不是写很长的代码(YBXIANG:指的是纯粹的JMX风格的代码)来访问一个远程MBean。这里,我们通过调用javax.management.JMX类的newMBeanProxy()方法,传入参数"MBean's MBeanServerConnection、object name、MBean接口的类名 以及 true(该参数意味着该proxy必须像一个NotificationBroadcaster那样)",为Hello创建了一个MBean proxy。现在,JMX client就能够执行由Hello定义的操作了,就好像注册在本地的MBean的操作。该JMX client也添加了一个通知监听器,当修改MBean的CacheSize属性的时候,该MBean就会发出一个通知。

通过代理(Proxies)对远程MXBeans执行操作

你可以为MXBeans创建代理,和创建MBeans代理完全一样。

...
 
ObjectName mxbeanName = new ObjectName ("com.example:type=QueueSampler");
QueueSamplerMXBean mxbeanProxy = JMX.newMXBeanProxy(mbsc, 
 mxbeanName, QueueSamplerMXBean.class);QueueSample queue1 = mxbeanProxy.getQueueSample();//可以返回组合对象!
 echo("\nQueueSample.Date = " + queue1.getDate());
echo("QueueSample.Head = " + queue1.getHead());
echo("QueueSample.Size = " + queue1.getSize());
echo("\nInvoke clearQueue() in QueueSampler MXBean...");
mxbeanProxy.clearQueue();QueueSample queue2 = mxbeanProxy.getQueueSample();echo("\nQueueSample.Date = " + queue2.getDate());
echo("QueueSample.Head = " + queue2.getHead());
echo("QueueSample.Size = " + queue2.getSize());

...

如上所示,要想为MXBean创建一个代理,你所需要做的一切只是调用 JMX.newMXBeanProxy,而不是newMBeanProxy。这个MXBean代理,mxbeanProxy,使得客户端能够触发 QueueSampler MXBean的操作,就好像注册在本地的MXBean的操作一样。
关闭连接

一旦JMX客户端获取到了它所需要的一切信息,并对位于远程JMX agent的MBean Server中的MBeans执行了所有的、必须的操作之后,就必须关闭相关连接。

    jmxc.close();

通过调用JMXConnector.close方法来关闭该连接。


To Run the Custom JMX Client Example

This example requires version 6 of the Java SE platform. To monitor the Main JMX agent remotely using a custom JMX client Client, follow these steps:

    If you have not done so already, save jmx_examples.zip into your work_dir directory.
    Unzip the bundle of sample classes by using the following command in a terminal window.

    unzip jmx_examples.zip

    Compile the example Java classes from within the work_dir directory.

    javac com/example/*.java

    Start the Main application, specifying the properties that expose Main for remote management:

    java -Dcom.sun.management.jmxremote.port=9999 \
     -Dcom.sun.management.jmxremote.authenticate=false \
     -Dcom.sun.management.jmxremote.ssl=false \
     com.example.Main

    A confirmation that Main is waiting for something to happen is generated.
    Start the Client application in a different terminal window:

    java com.example.Client

    A confirmation that an MBeanServerConnection has been obtained is displayed.
    Press Enter.

    The domains in which all the MBeans that are registered in the MBean server started by Main are displayed.
    Press Enter again.

    The number of MBeans that are registered in the MBean server is displayed, as well as the object names of all these MBeans. The MBeans displayed include all the standard platform MXBeans running in the Java VM, as well as the Hello MBean and the QueueSampler MXBean that were registered in the MBean server by Main.
    Press Enter again.

    The Hello MBean's operations are invoked by Client, with the following results:
        A notification listener is added to Client to listen for notifications from Main.
        The value of the CacheSize attribute is changed from 200 to 150.
        In the terminal window in which you started Main, confirmation of the CacheSize attribute change is displayed.
        In the terminal window in which you started Client, a notification from Main is displayed, informing Client of the CacheSize attribute change.
        The Hello MBean's sayHello operation is invoked.
        In the terminal window in which you started Main, the message "Hello world" is displayed.
        The Hello MBean's add operation is invoked, with the values 2 and 3 as parameters. The result is displayed by Client.
    Press Enter again.

    The QueueSampler MXBean's operations are invoked by Client, with the following results:
        The QueueSample values date, head, and size are displayed.
        The clearQueue operation is invoked.
    Press Enter again.

    The Client closes the connection to the MBean server and a confirmation is displayed.