JMX 入门(二)

来源:互联网 发布:数据分析师职业规划 编辑:程序博客网 时间:2024/06/05 10:58

JMX 入门(二)

创建 JMX 客户端

前面一篇中,通过 MBeanServer 发布的 JMX 服务称之为服务端,我们已经知道如何通过 JConsole 作为客户端连接 JMX 服务,在这一篇中,我们将通过 Java 编码的方式来写客户端。

创建一个JMSClient 类,在类中创建 main 方法,然后开始下面的代码。

String host = "127.0.0.1";int port = 9999;String url = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";JMXServiceURL serviceURL = new JMXServiceURL(url);final JMXConnector connector;try {  connector = JMXConnectorFactory.connect(serviceURL);} catch (IOException e) {  e.printStackTrace();  return;}MBeanServerConnection connection = connector.getMBeanServerConnection();

使用JMXConnectorFactory 方式连接时,JMXServiceURL 的参数 url 必须使用 service:jmx 方式进行连接,通过上面这种方式,我们得到了一个 JMX 客户端和服务器直接的连接 connection

遍历获取所有信息

下面,我们先通过遍历获取所有的 MBean 信息。

Set<ObjectName> objectNames = connection.queryNames(null, null);for (ObjectName objectName : objectNames) {  System.out.println("========" + objectName + "========");  MBeanInfo mBeanInfo = connection.getMBeanInfo(objectName);  System.out.println("[Attributes]");  for (MBeanAttributeInfo attr : mBeanInfo.getAttributes()) {    Object value = null;    try {      value = attr.isReadable() ? connection.getAttribute(objectName, attr.getName()) : "";    } catch (Exception e) {      value = e.getMessage();    }    System.out.println(attr.getName() + ":" + value);  }  System.out.println("[Operations]");  for (MBeanOperationInfo oper : mBeanInfo.getOperations()) {    System.out.println(oper.getName() + ":" + oper.getDescription());  }  System.out.println("[Notifications]");  for (MBeanNotificationInfo notice : mBeanInfo.getNotifications()) {    System.out.println(notice.getName() + ":" + notice.getDescription());  }}

首先通过 connectionqueryNames (方法参数为 null 时)获取所有的注册的 MBean 的名字,然后对名字进行遍历,根据名字获取 MBeanInfo,在遍历获取其中的属性、操作和通知信息。在获取属性的时候,我们先判断属性是否可读,然后在通过 objectName 和属性名获取值。

我们先根据上一篇博客的内容,使用 9999 端口启动 JMX 服务(命令过长时,win 使用 ^ 换行,linux 使用 \换行):

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

编译运行 JMSClient 类,然后就能看到控制台输出了大量的信息,部分内容如下:

========java.lang:type=MemoryPool,name=Metaspace                    ========[属性信息]Name:MetaspaceType:NON_HEAPValid:trueCollectionUsage:nullCollectionUsageThreshold:java.lang.UnsupportedOperationException: CollectionUsage threshold is not supportedCollectionUsageThresholdCount:java.lang.UnsupportedOperationException: CollectionUsage threshold is not supportedMemoryManagerNames:[Ljava.lang.String;@7eda2dbbPeakUsage:javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=10485760, init=0, max=-1, used=9806016})Usage:javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=10485760, init=0, max=-1, used=9806016})UsageThreshold:0UsageThresholdCount:0CollectionUsageThresholdExceeded:java.lang.UnsupportedOperationException: CollectionUsage threshold is not supportedCollectionUsageThresholdSupported:falseUsageThresholdExceeded:falseUsageThresholdSupported:trueObjectName:java.lang:type=MemoryPool,name=Metaspace[操作信息]resetPeakUsage:resetPeakUsage[通知信息]========java.lang:type=MemoryPool,name=PS Old Gen                   ========[属性信息]Name:PS Old GenType:HEAPValid:true

上面日志中的大部分信息都是 JVM 相关的内容,当前程序的 JVM 信息可以通过 java.lang.management.ManagementFactory 工厂类获取。

从日志中找一下 com.example 的相关信息如下。

========com.example:type=Hello                                      ========[属性信息]Name:ReginaldCacheSize:2006[操作信息]add:Operation exposed for managementsayHello:Operation exposed for management[通知信息]javax.management.AttributeChangeNotification:An attribute of this MBean has changed

获取指定的 MBean

通过上面遍历了解大概的信息后,我们以上面的创建 JMX 客户端为基础创建JMSClient2。根据我们Main 中发布的对象名,我们这里手动创建相同的一个名字:

ObjectName objectName = new ObjectName("com.example:type=Hello");

然后使用 connection 通过指定名来进行其他操作,首先我们订阅当前对象的通知。

connection.addNotificationListener(objectName, new NotificationListener() {  @Override  public void handleNotification(Notification notification, Object handback) {    System.out.println("\nReceived notification:");    System.out.println("\tClassName: " + notification.getClass().getName());    System.out.println("\tSource: " + notification.getSource());    System.out.println("\tType: " + notification.getType());    System.out.println("\tMessage: " + notification.getMessage());    if (notification instanceof AttributeChangeNotification) {      AttributeChangeNotification acn =        (AttributeChangeNotification) notification;      System.out.println("\tAttributeName: " + acn.getAttributeName());      System.out.println("\tAttributeType: " + acn.getAttributeType());      System.out.println("\tNewValue: " + acn.getNewValue());      System.out.println("\tOldValue: " + acn.getOldValue());    }  }}, null, null);

然后我们分别获取当前的值和修改当前的值,再调用提供的两个方法。

Object cacheSize = connection.getAttribute(objectName, "CacheSize");System.out.println("Get Value: " + cacheSize);connection.setAttribute(objectName, new Attribute("CacheSize", 100));connection.invoke(objectName, "sayHello", null, null);Object result = connection.invoke(objectName, "add",    new Object[]{1, 9},    new String[]{int.class.getCanonicalName(), int.class.getCanonicalName()});System.out.println("1 + 9 = " + result);

编译执行 JMSClient2,我们可以看到控制台输出的如下信息。

Get Value: 4519Received notification:    ClassName: javax.management.AttributeChangeNotification    Source: com.example:type=Hello    Type: jmx.attribute.change    Message: CacheSize changed    AttributeName: CacheSize    AttributeType: int    NewValue: 100    OldValue: 45191 + 9 = 10

获取当前值后,又修改了值,我们收到一个值改变的通知,调用方法时可以看到服务端输出端的日志和客户端获得的结果。

使用 MBean 接口调用

JMSClient2 的基础上,如果我们当前可以得到 HelloMBean 接口,还可以像下面这样调用。

HelloMBean mbeanProxy =  JMX.newMBeanProxy(connection, objectName, HelloMBean.class, true);System.out.println("Get Value: " + mbeanProxy.getCacheSize());mbeanProxy.setCacheSize(100);mbeanProxy.sayHello();int result = mbeanProxy.add(1, 9);System.out.println("1 + 9 = " + result);

这种方式和前面相比,调用更直接和简单,这里通过 JMX 创建动态代理,功能实现代码都在 MBeanServerInvocationHandler 类中,这个类根据调用的方法转换为 JMSClient2 中的方式进行调用,实现原理很简单(只要接口方法签名一致就能通过这种方式调用,不必是同一接口,和 Hessian的接口类似)。

这两种方式基本上就覆盖了 JMX 客户端的所有功能,类似 JConsole 的部分功能,实际上就是对 JMX 接口的调用和加工,通过 JMX 可以方便的监控资源和动态修改资源,还能在资源变化时使用通知提醒。这一篇的主要内容就是这些,可能会在下一篇中涉及SSL和用户密码登陆,还会使用一个很方便的 JMX 工具包。

代码下载

链接:http://pan.baidu.com/s/1skGR9Id 密码:mafm

原创粉丝点击