JMX入门(二)Java客户端

创建 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());
   }
 }
 首先通过 connection 的 queryNames (方法参数为 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:Metaspace
 Type:NON_HEAP
 Valid:true
 CollectionUsage:null
 CollectionUsageThreshold:java.lang.UnsupportedOperationException: CollectionUsage threshold is not supported
 CollectionUsageThresholdCount:java.lang.UnsupportedOperationException: CollectionUsage threshold is not supported
 MemoryManagerNames:[Ljava.lang.String;@7eda2dbb
 PeakUsage: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:0
 UsageThresholdCount:0
 CollectionUsageThresholdExceeded:java.lang.UnsupportedOperationException: CollectionUsage threshold is not supported
 CollectionUsageThresholdSupported:false
 UsageThresholdExceeded:false
 UsageThresholdSupported:true
 ObjectName:java.lang:type=MemoryPool,name=Metaspace
 [操作信息]
 resetPeakUsage:resetPeakUsage
 [通知信息]
 ========java.lang:type=MemoryPool,name=PS Old Gen                   ========
 [属性信息]
 Name:PS Old Gen
 Type:HEAP
 Valid:true
 上面日志中的大部分信息都是 JVM 相关的内容,当前程序的 JVM 信息可以通过 java.lang.management.ManagementFactory 工厂类获取。
从日志中找一下 com.example 的相关信息如下。
========com.example:type=Hello                                      ========
 [属性信息]
 Name:Reginald
 CacheSize:2006
 [操作信息]
 add:Operation exposed for management
 sayHello:Operation exposed for management
 [通知信息]
 javax.management.AttributeChangeNotification:An attribute of this MBean has changed
 1
 2
 3
 4
 5
 6
 7
 8
 9
 获取指定的 MBean
 通过上面遍历了解大概的信息后,我们以上面的创建 JMX 客户端为基础创建JMSClient2。根据我们Main 中发布的对象名,我们这里手动创建相同的一个名字:
ObjectName objectName = new ObjectName("com.example:type=Hello");
 1
 然后使用 connection 通过指定名来进行其他操作,首先我们订阅当前对象的通知。
connection.addNotificationListener(objectName, new NotificationListener() {
   @Override
   public void handleNotification(Notification notification, Object handback) {
     System.out.println("
Received notification:");
     System.out.println("	ClassName: " + notification.getClass().getName());
     System.out.println("	Source: " + notification.getSource());
     System.out.println("	Type: " + notification.getType());
     System.out.println("	Message: " + notification.getMessage());
     if (notification instanceof AttributeChangeNotification) {
       AttributeChangeNotification acn =
         (AttributeChangeNotification) notification;
       System.out.println("	AttributeName: " + acn.getAttributeName());
       System.out.println("	AttributeType: " + acn.getAttributeType());
       System.out.println("	NewValue: " + acn.getNewValue());
       System.out.println("	OldValue: " + 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: 4519
Received notification:
     ClassName: javax.management.AttributeChangeNotification
     Source: com.example:type=Hello
     Type: jmx.attribute.change
     Message: CacheSize changed
     AttributeName: CacheSize
     AttributeType: int
     NewValue: 100
     OldValue: 4519
 1 + 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 工具包。
 ————————————————
 版权声明:本文为CSDN博主「isea533」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
 原文链接:https://blog.csdn.net/isea533/article/details/77455973
以上是 JMX入门(二)Java客户端 的全部内容, 来源链接: utcz.com/z/511125.html

