原文地址: http://docs.oracle.com/javase/tutorial/jmx/remote/index.html
Lesson: Remote Management
JMX API能够确保你使用基于JMX技术的连接器(JMX connectors)去执行对资源的远程管理。JMX连接器使基于Java的远程客户端连接服务器成为了可能。一个作为连接器的客户端基本解耦和MBean server的相同。一个JMX连接器包括一个连接客户端和一个连接服务器。连接服务器是MBean服务器用于监听来自于客户端请求。连接器客户端用于和连接器服务器简历连接。连接器客户端和连接器服务器通常不运行在同一个Java虚拟机上。JMX API定义了基于RMI的标准连接协议。这个协议用于连接一个JMX客户端到MBean服务器,然后操作注册的MBean像在本地操作一样。
Java平台提供了一个使用JMX API的标准RMI连接器可以开箱即用的监控应用。开箱即用的RMI连接器自动的暴露应用给远程的管理不需要你显式的定义远程连接服务。开箱即用的管理代理在你启动应用的时候自动激活。基于JMX技术的监控和管理程序可以在远程监控这些应用。
Exposing a Resource for Remote Management By JConsole
如果你使用开箱即用的远程管理监控工具例如: JConsole, 暴露你的使用JMX API的应用给远程的管理非常简单。如果要暴露你的应用给远程的管理你需要用合适的方法启动。这个例子展示了如何暴露你的Main JMX代理给远程管理。
安全建议:
为了简单的目的,这个例子没有开启认证和加密。然而,你应该在实际的生产环境中你用该实现这些安全机制。接下来的文档提供了如何去激活安全机制。
这个例子需要JDK1.6或以后的版本。为了远程监控Main JMX代理,你需要下面的步骤:
1.如果你还没有做好,保存jmx_examples.zip到你的工作目录。
2.在终端窗口中解压成简单的类:
unzip jmx_examples.zip
3.在工作目录中编译这些类:
javac com/example/*.java
4.运行Main应用,指定属性去暴露应用给远程的管理。在windows里面使用(^)代替换行符(\) 来完成一个长命令的换行。
java -Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
com.example.Main
Main的等待事情发生的确认信息已经生成。
5.在不同的机器的不同终端窗口口打开JConsole
jconsole
6.选择远程进程,并且输入远程进程域:
hostname:9999
在这个地址里,hostName是Main程序运行的远程机器的IP,9999是进行远程管理的端口号。
7.点击连接
关于Main的JVM概况显示出来了
8.点击MBeans标签
这个面板展示了当前MBean server上面注册的所有MBean
9. 在窗体的左部,展开MBean树的com.example节点
你可以看到Hello已经被Main注册。如果你点击Hello,你可以看到MBean树节点的属性和操作,尽管他们运行在不同的机器上。
10.关闭并且退出JConsole
Creating a Custom JMX Client
之前的章节介绍了如何创建一个JMX技术的MBeans或MXBeans,然后注册他们到JMX代理。然而,所有的例子都使用存在的JMX客户端,这节介绍了如何创建一个自定义的JMX客户端。一个通用客户端的例子,client被包括在jmx_examples.zip。这个JMX客户端和之前章节里面相同的MBean,MXBean和JMX代理交互。
导入远程JMX API类
为了让你的JMX客户端具备连接远程JMX代理的能力,你需要使用javax.management.remote包下的这些类。
package com.example;
...
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class Client {
...
这个客户端创建了一个JMXConnector实例,他需要一个JMXConnectorFactory和JMXServiceURL。
创建一个通知监听器
JMX客户端需要监听并且处理一些在JMX代理的MBean server中注册的MBeans发送的消息。JMX客户端的通知处理是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类型的通知,通过调用AttributeChangeNotification的getAttributeName, getAttributeType, getNewValue 和 getOldValue方法可以获取改变属性的名字,类型和新、老值.
可以通过下面的代码创建一个新的ClientListener实例。
ClientListener listener = new ClientListener();
创建一个RMI连接器客户端
这个客户端创建了一个RMI连接去连接Main代理创建的RMI连接服务器。它允许JMX客户端和JMX代理运行在同一台机器上。
...
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);
...
正如你看到的,client定义了一个名字叫做url的JMXServiceURL实例,它描述了客户端想要连接的服务器的位置。这个URL允许客户端检索注册在9999端口的本地和远程地址jmx rmi服务。
伴随着唯一的RMI注册表,连接器客户端也被创建。jmxc连接客户端是JMXConnector的一个实例,调用JMXConnectorFactory的connect()方法创建。调用connect()方法的时候传递了一个url参数和null的环境map。
连接到远程的MBean服务器
RMI连接被创建的时候,JMX客户端必须连接到远程的MBean server,以便他可以和远程JMX代理上注册的各种MBeans交互。
...
MBeanServerConnection mbsc =
jmxc.getMBeanServerConnection();
...
一个叫做mbsc的MBeanServerConnection被创建,通过调用JMXConnector实例jmxc的getMBeanServerConnection()方法。
现在连接客户端已经连接到了MBean server创建的JMX代理,并且操作注册的MBeans对两头都是透明的。
首先,客户端定义了一些简单的操作来获得MBean代理上的MBeans信息。
...
echo("\nDomains:");
String domains[] = mbsc.getDomains();
Arrays.sort(domains);
for (String domain : 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的许多方法去获取MBean server上面注册的MBeans数量、要操作的域以及每个MBeans的对象名字。
通过MBeans代理在远程MBeans上执行操作
客户端通过调用Hello MBean的代理去和远程的MBean server交互。这个MBean代理位于本地,模仿了远程的MBean。
...
ObjectName mbeanName = new ObjectName("com.example:type=Hello");
HelloMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName,
HelloMBean.class, true);
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代理允许你通过Java接口访问MBean而不是写重量级的访问远程MBean代码。Hello的MBean代理被通过调用javax.management.JMX类的newMBeanProxy()方法创建,传递了MBean的MBeanServerConnection,对戏那个名字和true,意味着代理是一个NotificationBroadcaster。JMX客户端现在可以执行Hello定义的操作就像本地注册的MBean一样。JMX客户端也可以增加一个通知监听器然后改变MBean的CacheSize属性,然后发送一个通知。
你也可以想创建MBeans代理一样创建MXBeans代理。
...
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 允许客户端调用QueueSample 的操作像在本地一样。
关闭连接
一旦JMX客户端获取了所有的需要在远程JMX代理上面注册的MBeans的信息,这个连接诶必须被关闭。
jmxc.close();
关闭连接需要调用JMXConnector.close方法。
运行自定义的JMX客户端例子
这个例子运行在JDK1.6或以后的版本,为了使用JMX客户端client检测远程的JMX代理Main,你需要做以下几步:
1.如果你还没有做好,保存jmx_examples.zip到你的工作目录。
2.在终端窗口中输入命令解压文件
unzip jmx_examples.zip
3.在工作目录中编译这些类
javac com/example/*.java
4.启动Main应用
java -Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
com.example.Main
5.在不同的终端窗口中开启Client
java com.example.Client
MBeanServerConnection获取的信息被展示
6.按Enter(回车)
Main的MBean server上注册的所有MBeans被展示出来
7.再按一次回车
MBean serve上注册的MBeans数目展示出来了。这些MBeans的对象名字也被展示出来。包括Main注册的Hello MBean和QueueSampler MXBean。
8.再按一次回车
Client调用了Hello MBean的操作:
一个通知监听器被添加到Client用于监听Main发出的通知
CacheSize属性从200变为150
在开启Main的终端窗口中,CacheSize属性改变的信息被展示。
在开启Client的终端窗口中,Main的通知 CacheZize属性改变的通知被展示
HelloMBean的sayHello操作被调用
在开启Main的终端窗口中,“Hello world”信息被展示
使用参数2和3调用Hello MBean的add操作,然后这个结果展示在Client
9.再按一次回车
Client调用了QueueSampler MBean的操作:
QueueSampler的date,head和size被展示。
clearQueue操作被调用
10.再按一次回车
Cilent客户端被关闭。