偶尔记一下

没事看看 - MyBatis工具

JMX 入门(二)Java客户端

这篇博客参考官方教程以及个人的理解,通过实际的代码和操作来学会使用 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: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

获取指定的 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: 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 工具包。

代码下载

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

阅读更多

扫码向博主提问

去开通我的Chat快问

isea533

博客专家

MyBatis相关答疑
  • 擅长领域:
  • MyBatis
  • Spring Boo
  • Spring
版权声明:版权归博主所有,转载请带上本文链接!联系方式:abel533@gmail.com https://blog.csdn.net/isea533/article/details/77455973
文章标签: java jmx
个人分类: JAVA编程
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭