Java管理扩展JMX入门学习

6 篇文章 1 订阅

系列文章目录

Java管理扩展JMX入门学习
Spring JMX编程学习(一)- 手动注册bean到MBeanServer
Spring JMX编程学习(二)- 以Bean的方式注册MbeanServer
Spring JMX编程学习(三)- 自定义JMX客户端
Spring JMX编程学习(四)- MBeans自动探测与注解
Spring JMX编程学习(五)- SpringBoot自动注册



前言

Java管理扩展(JMX)(Java Management Extensions)技术是Java平台标准版(Java SE平台)的标准部分。JMX技术已在Java 2 Platform Standard Edition(J2SE)5.0版本中添加到该平台。

JMX技术提供了一种简单、标准的方法来管理资源,例如应用程序(applications),设备(devices)和服务(services)。由于JMX技术是动态的,因此可以在创建、安装和实施资源时使用它来监视和管理资源。您还可以使用JMX技术来监视和管理Java虚拟机(Java VM)。

JMX规范使用Java编程语言定义了体系结构,设计模式,API和服务,用于管理和监视应用程序和网络。

使用JMX技术,给定资源由一个或多个称为“Managed Beans”或MBean的Java对象检测。这些MBean在核心管理的对象服务器(称为MBean服务器)中注册。MBean服务器充当管理代理,并且可以在启用了Java编程语言的大多数设备上运行。

这些规范定义了用于管理已正确配置用于管理的任何资源的JMX代理。 JMX代理由其中注册了MBean的MBean服务器和用于处理MBean的一组服务组成。这样,JMX代理可以直接控制资源,并使资源可用于远程管理应用程序。

检测资源的方式完全独立于管理基础架构。因此,无论如何实现其管理应用程序,都可以使资源变得可管理。

JMX技术定义了标准连接器(称为JMX连接器 JMX connectors),使您可以从远程管理应用程序访问JMX代理。使用不同协议的JMX连接器提供相同的管理接口。因此,无论使用何种通信协议,管理应用程序都可以透明地管理资源。不符合JMX规范的系统或应用程序也可以使用JMX代理,只要这些系统或应用程序支持JMX代理即可。


提示:以下是本篇文章正文内容,下面案例可供参考

一、标准MBeans

1. 创建MBean接口

package com.example.jmx;

/**
 * By convention, an MBean interface takes the name of the Java class that implements it,
 * with the suffix MBean added
 */
public interface HelloMBean {

    // declares two operations: the Java methods add() and sayHello()

    void sayHello();

    int add(int x, int y);

    // two attributes: Name is a read-only string, and CacheSize is an integer that can be both read and written

    String getName();

    int getCacheSize();

    void setCacheSize(int size);
} 

2. MBean实现类

package com.example.jmx;

public class Hello implements HelloMBean {
    private static final int
            DEFAULT_CACHE_SIZE = 200;
    private final String name = "Reginald";
    private int cacheSize = DEFAULT_CACHE_SIZE;

    @Override
    public void sayHello() {
        System.out.println("hello, world");
    }

    @Override
    public int add(int x, int y) {
        return x + y;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public int getCacheSize() {
        return this.cacheSize;
    }

    @Override
    public synchronized void setCacheSize(int size) {
        this.cacheSize = size;
        System.out.println("Cache size now " + this.cacheSize);
    }
}

3. 创建一个JMX代理用于管理资源

package com.example.jmx;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;

public class Main {

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

        // obtaining an MBean server created and initialized by the platform
        //  If no MBean server has been created by the platform already, then getPlatformMBeanServer() creates an MBean
        //  server automatically by calling the JMX method MBeanServerFactory.createMBeanServer()
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        // 每个Mbean都必须包含ObjectName
        ObjectName name = new ObjectName("com.example.jmx:type=Hello");
        Hello mbean = new Hello();
        mbs.registerMBean(mbean, name);

        System.out.println("Waiting forever...");
        Thread.sleep(Long.MAX_VALUE);
    }
}

在以上的main方法当中,通过java.lang.management.ManagementFactory#getPlatformMBeanServer方法获取一个已经创建和初始化的MBean server,如果当前平台还没有创建的话,该方法会自动调用javax.management.MBeanServerFactory#createMBeanServer方法创建一个。并同时注册一些JDK中自带的MXBeans,比如java.lang.management.MemoryPoolMXBeanjava.lang.management.MemoryManagerMXBean.创建MBeanServer之后,定义了一个MBean实例,每个MBean实例必须包含一个ObjectName,这个对象的值必须按照JMX的标准来定义,比如上面的com.example.jmx:type=Hello就包含包名、双引号、type、等号和MBean的名称。然后创建一个mbean对象,并通过javax.management.MBeanServer#registerMBean方法注册到MBeanServer当中。

private static MBeanServer platformMBeanServer;

public static synchronized MBeanServer getPlatformMBeanServer() {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        Permission perm = new MBeanServerPermission("createMBeanServer");
        sm.checkPermission(perm);
    }

    // 如果platformMBeanServer还不存在 就会创建一个
    if (platformMBeanServer == null) {
        platformMBeanServer = MBeanServerFactory.createMBeanServer();
        // 自动注册一些平台的MXBeans 比如MemoryPoolMXBean和MemoryManagerMXBean
        for (PlatformComponent pc : PlatformComponent.values()) {
            List<? extends PlatformManagedObject> list =
                pc.getMXBeans(pc.getMXBeanInterface());
            for (PlatformManagedObject o : list) {
                // Each PlatformComponent represents one management
                // interface. Some MXBean may extend another one.
                // The MXBean instances for one platform component
                // (returned by pc.getMXBeans()) might be also
                // the MXBean instances for another platform component.
                // e.g. com.sun.management.GarbageCollectorMXBean
                //
                // So need to check if an MXBean instance is registered
                // before registering into the platform MBeanServer
                if (!platformMBeanServer.isRegistered(o.getObjectName())) {
                    addMXBean(platformMBeanServer, o);
                }
            }
        }
        HashMap<ObjectName, DynamicMBean> dynmbeans =
                ManagementFactoryHelper.getPlatformDynamicMBeans();
        for (Map.Entry<ObjectName, DynamicMBean> e : dynmbeans.entrySet()) {
            addDynamicMBean(platformMBeanServer, e.getValue(), e.getKey());
        }
        for (final PlatformManagedObject o :
                                   ExtendedPlatformComponent.getMXBeans()) {
            if (!platformMBeanServer.isRegistered(o.getObjectName())) {
                addMXBean(platformMBeanServer, o);
            }
        }
    }
    // 已经存在 则直接返回
    return platformMBeanServer;
}

4. JConsole作为客户端管理

在jdk的bin目录下查找到jconsole小工具,然后打开
在这里插入图片描述
选择到自己的进程,并点击下面的连接,连接过程提示安全问题,可以忽略,选用不安全的连接.
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
这个jConsol也称为监视和管理控制台,工具栏中包含有一些内存线程这些信息,其实都是通过JMX技术从服务端获取的(系统默认注册)。我们现在看mBean这个选项。很容易就会发现我们自己注册的mBean(com.example.jmx.Hello)。在mBeanInfo当中我们可以看到ObjectNameClassName信息,另外在描述符里面也有interfaceClassName这些信息。
在这里插入图片描述
在左侧我们可以看到这个mBean的属性和操作,其实在定义mBean接口的时候,我们通过setter/getter方法来定义属性(Attributes),如果只有getter方法,这个属性就是只读的,包含setter/getter方法才是可写可读,如果不是setter/getter方法那么就属于操作(Operations)。比如上面的add和sayHello。
对于属性cacheSize,就是可读可写的,当前的值为200.
在这里插入图片描述
尝试修改这个属性,被监视和管理的程序内存值就被改变了。
在这里插入图片描述
Name这个属性是不可写入的,因为com.example.jmx.HelloMBean接口中没有提供setter方法。
在这里插入图片描述
接下来我们再看一下操作,通过jConsole来执行一下操作。
在这里插入图片描述
在这里插入图片描述

5. 通知

JMX API定义了一种机制,使MBean能够生成通知,例如,以信号通知状态更改,检测到的事件或问题。

要生成通知,MBean必须实现接口NotificationEmitter或扩展NotificationBroadcasterSupport。 要发送通知,您需要构造类javax.management.Notification的实例或子类(例如AttributeChangedNotification),然后将该实例传递给NotificationBroadcasterSupport.sendNotification。

每个通知都有一个来源。 源是生成通知的MBean的对象名称。

每个通知都有一个序列号。 当订单很重要时,此数字可用于订购来自同一来源的通知,并且存在以错误的顺序处理通知的风险。 序列号可以为零,但是对于给定MBean的每个通知,序列号最好递增。

修改MBean定义

package com.example.jmx;

import javax.management.AttributeChangeNotification;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;

public class Hello
        extends NotificationBroadcasterSupport implements HelloMBean {

    private static final int DEFAULT_CACHE_SIZE = 200;
    private final String name = "Reginald";
    private int cacheSize = DEFAULT_CACHE_SIZE;
    private long sequenceNumber = 1;

    @Override
    public void sayHello() {
        System.out.println("hello, world");
    }

    @Override
    public int add(int x, int y) {
        return x + y;
    }

    /* Getter for the Name attribute. The pattern shown here is frequent: the
       getter returns a private field representing the attribute value. In our
       case, the attribute value never changes, but for other attributes it
       might change as the application runs. Consider an attribute representing
       statistics such as uptime or memory usage, for example. Being read-only
       just means that it can't be changed through the management interface. */
    @Override
    public String getName() {
        return this.name;
    }

    /* Getter for the CacheSize attribute. The pattern shown here is
       frequent: the getter returns a private field representing the
       attribute value, and the setter changes that field. */
    @Override
    public int getCacheSize() {
        return this.cacheSize;
    }

    /* Setter for the CacheSize attribute. To avoid problems with
       stale values in multithreaded situations, it is a good idea
       for setters to be synchronized. */
    @Override
    public synchronized void setCacheSize(int size) {
        int oldSize = this.cacheSize;
        this.cacheSize = size;

	/* In a real application, changing the attribute would
	   typically have effects beyond just modifying the cacheSize
	   field.  For example, resizing the cache might mean
	   discarding entries or allocating new ones. The logic for
	   these effects would be here. */
        System.out.println("Cache size now " + this.cacheSize);

	/* Construct a notification that describes the change. The
	   "source" of a notification is the ObjectName of the MBean
	   that emitted it. But an MBean can put a reference to
	   itself ("this") in the source, and the MBean server will
	   replace this with the ObjectName before sending the
	   notification on to its clients.

	   For good measure, we maintain a sequence number for each
	   notification emitted by this MBean.

	   The oldValue and newValue parameters to the constructor are
	   of type Object, so we are relying on Tiger's autoboxing
	   here.  */
        Notification n =
                new AttributeChangeNotification(this,
                        sequenceNumber++,
                        System.currentTimeMillis(),
                        "CacheSize changed",
                        "CacheSize",
                        "int",
                        oldSize,
                        this.cacheSize);

	/* Now send the notification using the sendNotification method
	   inherited from the parent class NotificationBroadcasterSupport. */
        sendNotification(n);
    }

    @Override
    public MBeanNotificationInfo[] getNotificationInfo() {
        String[] types = new String[]{
                AttributeChangeNotification.ATTRIBUTE_CHANGE
        };
        String name = AttributeChangeNotification.class.getName();
        String description = "An attribute of this MBean has changed";
        MBeanNotificationInfo info =
                new MBeanNotificationInfo(types, name, description);
        return new MBeanNotificationInfo[]{info};
    }
}

以上AttributeChangeNotification参数:
在这里插入图片描述
重启通过JConsole连接
在这里插入图片描述
点击订阅
在这里插入图片描述
在com.example.jmx.Hello#setCacheSize方法中会发送通知(sendNotification),因为修改属性
在这里插入图片描述
在这里插入图片描述

通过以上的小案例,我们展示了MBeans的使用。总结一下:

MBean是类似于JavaBeans组件的托管Java对象,它遵循JMX规范中阐述的设计模式。 MBean可以代表设备,应用程序或任何需要管理的资源。 MBean公开了一个包含以下内容的管理接口:

  • 一组可读或可写的属性,或两者兼而有之。
  • 一组可调用的操作。
  • 自我描述。

在MBean实例的整个生命周期中,管理界面都不会更改。 当某些预定义事件发生时,MBeans也可以发出通知。

二、MXBeans

MXBean是一种MBean,仅引用一组预定义的数据类型。 这样,您可以确保您的MBean可被任何客户端(包括远程客户端)使用,而无需客户端有权访问代表MBeans类型的特定于模型的类。 MXBean提供了一种方便的方式将相关值捆绑在一起,而无需将客户端进行特殊配置以处理捆绑。

以与标准MBean相同的方式,通过编写称为SomethingMXBean的Java接口和实现该接口的Java类来定义MXBean。 但是,与标准MBean不同,MXBean不需要将Java类称为Something。 接口中的每个方法都定义MXBean中的属性或操作。 注释@MXBean也可以用于注释Java接口,而不是要求接口名称后跟MXBean后缀。

1. 自定义数据类型

创建一个自定义的数据类型:

/*
 * QueueSample.java - Java type representing a snapshot of a given queue.
 * It bundles together the instant time the snapshot was taken, the queue
 * size and the queue head.
 */

package com.example.mxb;

import java.beans.ConstructorProperties;
import java.util.Date;

public class QueueSample {
    
    private final Date date;
    private final int size;
    private final String head;

    /**
     * convert the given instance into a CompositeData instance and uses the @ConstructorProperties annotation to
     * reconstruct a QueueSample instance from a CompositeData instance
     */
    @ConstructorProperties({"date", "size", "head"})
    public QueueSample(Date date, int size, String head) {
        this.date = date;
        this.size = size;
        this.head = head;
    }

    public Date getDate() {
        return date;
    }

    public int getSize() {
        return size;
    }

    public String getHead() {
        return head;
    }
}

2. 创建MXBean接口

创建一个MXBean接口:

package com.example.mxb;

public interface QueueMXBean {
    public QueueSample getQueueSample();

    public void clearQueue();
}

3. 创建MXBean实例

创建一个MXBean实例,注意这里的实例不需要名称为Queue(接口名称除去MXBean部分),可以是任意其他名字,此处使用QueueSampler

package com.example.mxb;

import java.util.Date;
import java.util.Queue;

public class QueueSampler implements QueueMXBean {

    private Queue<String> queue;

    public QueueSampler(Queue<String> queue) {
        this.queue = queue;
    }

    @Override
    public QueueSample getQueueSample() {
        synchronized (queue) {
            return new QueueSample(new Date(), queue.size(), queue.peek());
        }
    }

    @Override
    public void clearQueue() {
        synchronized (queue) {
            queue.clear();
        }
    }
}

4. 创建JMX代理管理资源

代码如下(与MBean注册方式是一模一样的):

package com.example.mxb;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

public class Main {
    /* For simplicity, we declare "throws Exception".
       Real programs will usually want finer-grained exception handling. */
    public static void main(String[] args) throws Exception {
        // Get the Platform MBean Server
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        
        // Construct the ObjectName for the QueueSampler MXBean we will register
        ObjectName mxbeanName = new ObjectName("com.example.mxb:type=QueueSampler");

        // Create the Queue Sampler MXBean
        Queue<String> queue = new ArrayBlockingQueue<String>(10);
        queue.add("Request-1");
        queue.add("Request-2");
        queue.add("Request-3");
        QueueSampler mxbean = new QueueSampler(queue);

        // Register the Queue Sampler MXBean
        mbs.registerMBean(mxbean, mxbeanName);

        // Wait forever
        System.out.println("Waiting for incoming requests...");
        Thread.sleep(Long.MAX_VALUE);
    }
}

运行main程序,通过Jconsole查看
在这里插入图片描述

  • 查看属性

在这里插入图片描述
在这里插入图片描述
可以点击刷新按钮获取最新的值

  • 查看操作

在这里插入图片描述
原来队列中是有三个元素的,在清空队列之后,再去查看
在这里插入图片描述
总结一下,MXBean的操作和MBean其实是差不多的,就是在规范方面不同而已,比如实例名称与接口不需要硬性符合某种关系,另外可以使用自己定义的数据接口传输属性。


三、远程连接与自定义JMX客户端

1. 通过端口暴露程序用于远程管理

JMX技术允许我们通过JMX connectors进行远程管理,在上面的几个案例当中其实JConsole和被管理的程序使用的其实是一个JVM实例。如果是远程管理,应该是属于不同的JVM实例。

JMX连接器使MBean服务器可供基于Java技术的远程客户端访问。连接器的客户端导出与MBean服务器基本相同的接口。

JMX连接器由连接器客户端和连接器服务器组成。连接器服务器连接到MBean服务器,并侦听来自客户端的连接请求。连接器客户端负责与连接器服务器建立连接。连接器客户端通常与连接器服务器位于不同的Java虚拟机(Java VM)中,并且通常在不同的计算机上运行。 JMX API定义了基于远程方法调用(RMI)的标准连接协议。使用此协议,您可以将JMX客户端从远程位置连接到MBean服务器中的MBean,并在MBean上执行操作,就像在本地执行操作一样。

Java SE平台提供了一种开箱即用的方法,可以使用JMX API的标准RMI连接器来远程监视应用程序。开箱即用的RMI连接器自动公开用于远程管理的应用程序,而无需您自己创建专用的远程连接器服务器。通过使用正确的属性启动Java应用程序来激活即用型远程管理代理。然后,与JMX技术兼容的监视和管理应用程序可以连接到这些应用程序并进行远程监视。

首先将以上两个小程序合并到一起,使用一个主程序启动

/*
 * Main.java - main class for the Hello MBean and QueueSampler MXBean example.
 * Create the Hello MBean and QueueSampler MXBean, register them in the platform
 * MBean server, then wait forever (or until the program is interrupted).
 */

package com.example.notify;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

public class Main {
    /* For simplicity, we declare "throws Exception".
       Real programs will usually want finer-grained exception handling. */
    public static void main(String[] args) throws Exception {
        // Get the Platform MBean Server
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

        // Construct the ObjectName for the Hello MBean we will register
        ObjectName mbeanName = new ObjectName("com.example.notify:type=Hello");

        // Create the Hello World MBean
        Hello mbean = new Hello();

        // Register the Hello World MBean
        mbs.registerMBean(mbean, mbeanName);

        // Construct the ObjectName for the QueueSampler MXBean we will register
        ObjectName mxbeanName = new ObjectName("com.example.notify:type=QueueSampler");

        // Create the Queue Sampler MXBean
        Queue<String> queue = new ArrayBlockingQueue<String>(10);
        queue.add("Request-1");
        queue.add("Request-2");
        queue.add("Request-3");
        QueueSampler mxbean = new QueueSampler(queue);

        // Register the Queue Sampler MXBean
        mbs.registerMBean(mxbean, mxbeanName);

        // Wait forever
        System.out.println("Waiting for incoming requests...");
        Thread.sleep(Long.MAX_VALUE);
    }
}

并通过以下方式运行主程序(模拟远程端口为9999)

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

注意需要在项目根目录运行
在这里插入图片描述
当然也可以通过IDE设置系统参数的方式(比如IDEA当中)
在这里插入图片描述
启动完成之后,通过JConsole远程连接
在这里插入图片描述
在这里插入图片描述

2. 自定义JMX客户端管理程序

本教程的先前课程向您展示了如何创建JMX技术MBean和MXBean,以及如何向JMX代理注册它们。 但是,所有前面的示例都使用了现有的JMX客户端JConsole。 本课将演示如何创建自己的自定义JMX客户端。

package com.example.notify;

import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;

public class Client {

    /* For simplicity, we declare "throws Exception".
       Real programs will usually want finer-grained exception handling. */
    public static void main(String[] args) throws Exception {
        // Create an RMI connector client and
        // connect it to the RMI connector server
        //

首先通过javax.management.remote.JMXConnectorFactory#connect工厂方法获取一个JMXConnector对象。创建这个对象主要是用于创建一个MBeanServerConnection.

        echo("\nCreate an RMI connector client and " +
                "connect it to the RMI connector server");

        // 前提是开启MBeanXServer java -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false com.example.notify.Main
        JMXServiceURL url =
                new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null);

        // Get an MBeanServerConnection
        // 获取连接
        echo("\nGet an MBeanServerConnection");
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        waitForEnterPressed();

获取连接之后,就可以获取到已经注册的MBean了,方法为javax.management.MBeanServerConnection#getDomains。还有其他的一些有用接口getDefaultDomain、getMBeanCount等。

        // Get domains from MBeanServer
        //
        echo("\nDomains:");
        String domains[] = mbsc.getDomains();
        Arrays.sort(domains);
        for (String domain : domains) {
            echo("\tDomain = " + domain);
        }
        waitForEnterPressed();

        // Get MBeanServer's default domain
        //
        echo("\nMBeanServer default domain = " + mbsc.getDefaultDomain());

        // Get MBean count
        //
        echo("\nMBean count = " + mbsc.getMBeanCount());

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

以上代码运行结果如下

Create an RMI connector client and connect it to the RMI connector server

Get an MBeanServerConnection

Press <Enter> to continue...


Domains:
	Domain = JMImplementation
	Domain = com.example.notify
	Domain = com.sun.management
	Domain = java.lang
	Domain = java.nio
	Domain = java.util.logging

Press <Enter> to continue...


MBeanServer default domain = DefaultDomain

MBean count = 24

Query MBeanServer MBeans:
	ObjectName = JMImplementation:type=MBeanServerDelegate
	ObjectName = com.example.notify:type=Hello
	ObjectName = com.example.notify:type=QueueSampler
	ObjectName = com.sun.management:type=DiagnosticCommand
	ObjectName = com.sun.management:type=HotSpotDiagnostic
	ObjectName = java.lang:type=ClassLoading
	ObjectName = java.lang:type=Compilation
	ObjectName = java.lang:type=GarbageCollector,name=PS MarkSweep
	ObjectName = java.lang:type=GarbageCollector,name=PS Scavenge
	ObjectName = java.lang:type=Memory
	ObjectName = java.lang:type=MemoryManager,name=CodeCacheManager
	ObjectName = java.lang:type=MemoryManager,name=Metaspace Manager
	ObjectName = java.lang:type=MemoryPool,name=Code Cache
	ObjectName = java.lang:type=MemoryPool,name=Compressed Class Space
	ObjectName = java.lang:type=MemoryPool,name=Metaspace
	ObjectName = java.lang:type=MemoryPool,name=PS Eden Space
	ObjectName = java.lang:type=MemoryPool,name=PS Old Gen
	ObjectName = java.lang:type=MemoryPool,name=PS Survivor Space
	ObjectName = java.lang:type=OperatingSystem
	ObjectName = java.lang:type=Runtime
	ObjectName = java.lang:type=Threading
	ObjectName = java.nio:type=BufferPool,name=direct
	ObjectName = java.nio:type=BufferPool,name=mapped
	ObjectName = java.util.logging:type=Logging

Press <Enter> to continue...

其中就包含我们创建的
Domain = com.example.notify和ObjectName = com.example.notify:type=Hello、ObjectName = com.example.notify:type=QueueSampler,接下来我们在客户端通过创建Mbean的代理(javax.management.JMX#newMBeanProxy(javax.management.MBeanServerConnection, javax.management.ObjectName, java.lang.Class<T>, boolean))、监听然后修改属性。

        // ----------------------
        // Manage the Hello MBean
        // ----------------------

        echo("\n>>> Perform operations on Hello MBean <<<");

        // Construct the ObjectName for the Hello MBean
        //
        ObjectName mbeanName = new ObjectName("com.example.notify:type=Hello");

        // Create a dedicated proxy for the MBean instead of
        // going directly through the MBean server connection
        // 创建一个远程代理
        HelloMBean mbeanProxy =
                JMX.newMBeanProxy(mbsc, mbeanName, HelloMBean.class, true);

        // Add notification listener on Hello MBean
        //
        echo("\nAdd notification listener...");

        // Create listener
        // 创建一个监听器 所谓的订阅
        ClientListener listener = new ClientListener();
        mbsc.addNotificationListener(mbeanName, listener, null, null);

        // Get CacheSize attribute in Hello MBean
        // 获取值
        echo("\nCacheSize = " + mbeanProxy.getCacheSize());

        // Set CacheSize attribute in Hello MBean
        // Calling "reset" makes the Hello MBean emit a
        // notification that will be received by the registered
        // ClientListener.
        // 设置值
        mbeanProxy.setCacheSize(150);

        // Sleep for 2 seconds to have time to receive the notification
        //
        echo("\nWaiting for notification...");
        sleep(2000);

        // Get CacheSize attribute in Hello MBean
        //
        echo("\nCacheSize = " + mbeanProxy.getCacheSize());

        // Invoke "sayHello" in Hello MBean
        //
        echo("\nInvoke sayHello() in Hello MBean...");
        mbeanProxy.sayHello();

        // Invoke "add" in Hello MBean
        //
        echo("\nInvoke add(2, 3) in Hello MBean...");
        echo("\nadd(2, 3) = " + mbeanProxy.add(2, 3));

        waitForEnterPressed();

        // ------------------------------
        // Manage the QueueSampler MXBean
        // ------------------------------

        echo("\n>>> Perform operations on QueueSampler MXBean <<<");

        // Construct the ObjectName for the QueueSampler MXBean
        //
        ObjectName mxbeanName =
                new ObjectName("com.example.notify:type=QueueSampler");

        // Create a dedicated proxy for the MXBean instead of
        // going directly through the MBean server connection
        //
        QueueSamplerMXBean mxbeanProxy =
                JMX.newMXBeanProxy(mbsc, mxbeanName, QueueSamplerMXBean.class);

        // Get QueueSample attribute in QueueSampler MXBean
        //
        QueueSample queue1 = mxbeanProxy.getQueueSample();
        echo("\nQueueSample.Date = " + queue1.getDate());
        echo("QueueSample.Head = " + queue1.getHead());
        echo("QueueSample.Size = " + queue1.getSize());

        // Invoke "clearQueue" in QueueSampler MXBean
        //
        echo("\nInvoke clearQueue() in QueueSampler MXBean...");
        mxbeanProxy.clearQueue();

        // Get QueueSample attribute in QueueSampler MXBean
        //
        QueueSample queue2 = mxbeanProxy.getQueueSample();
        echo("\nQueueSample.Date = " + queue2.getDate());
        echo("QueueSample.Head = " + queue2.getHead());
        echo("QueueSample.Size = " + queue2.getSize());

        waitForEnterPressed();

        // Close MBeanServer connection
        // 关闭连接
        echo("\nClose the connection to the server");
        jmxc.close();
        echo("\nBye! Bye!");
    }

    private static void echo(String msg) {
        System.out.println(msg);
    }

    private static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void waitForEnterPressed() {
        try {
            echo("\nPress <Enter> to continue...");
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Inner class that will handle the notifications.
     */
    public static class ClientListener implements NotificationListener {
        @Override
        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());
            }
        }
    }
}

执行结果如下



>>> Perform operations on Hello MBean <<<

Add notification listener...

CacheSize = 200

Waiting for notification...

Received notification:
	ClassName: javax.management.AttributeChangeNotification
	Source: com.example.notify:type=Hello
	Type: jmx.attribute.change
	Message: CacheSize changed
	AttributeName: CacheSize
	AttributeType: int
	NewValue: 150
	OldValue: 200

CacheSize = 150

Invoke sayHello() in Hello MBean...

Invoke add(2, 3) in Hello MBean...

add(2, 3) = 5

Press <Enter> to continue...


>>> Perform operations on QueueSampler MXBean <<<

QueueSample.Date = Thu Nov 19 14:34:02 CST 2020
QueueSample.Head = Request-1
QueueSample.Size = 3

Invoke clearQueue() in QueueSampler MXBean...

QueueSample.Date = Thu Nov 19 14:34:02 CST 2020
QueueSample.Head = null
QueueSample.Size = 0

Press <Enter> to continue...


Close the connection to the server

Bye! Bye!

Process finished with exit code 0

总结

以上案例来自官方教程实例:https://docs.oracle.com/javase/tutorial/jmx/TOC.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值