【commons-pool2源码】_pre JMX

目录

  • 1、定义
  • 2、demo
  • 3、JMX在commons-pool2中的应用

1、定义

JMX(Java Management Extensions)html

简单来讲JMX技术提供了一套轻量的标准化的资源管理方式. 什么是资源的管理? 就是资源的增删改查!java

使用JMX技术, 能够管理和监控Java虚拟机的资源(内存, CPU, 线程等). 也能够将符合规范的 Managed Bean(MBean) 对象注册到MBean服务器, MBean服务器做为JMX代理, 本地或者远程控制MBean.chrome

JDK自带的jconsole工具就能够监控管理java应用中的MBean. jconsole工具位于%JAVA_HOME%\bin\目录下.apache

若是须要更准确更详细的了解JMX技术, 强烈建议阅读官方文档. 英文看起来吃力能够使用chrome浏览器, 右键翻译成中文.数组

2、demo

demo使用JMX实如今不重启应用的状况下, 动态修改对象的属性值. 该方法能够用于动态修改java应用中的配置.浏览器

demo目录
demo目录服务器

将MBean对象注册到MBeanServer中须要知足两个条件之一: 实现DynamicMBean接口 或者 自定义符合标准MBean规范的接口. demo中咱们采用自定义MBean接口.oracle

  1. 定义MBean接口AppConfigMBean. getB()表示B属性可读, setB()表示B属性可写. 这里暂时只测试一些基本类型.
package com.cztruth.jmx;
/**
 * MBean接口规范, 详见 `com.sun.jmx.mbeanserver.Introspector.checkCompliance(Class<?>![](https://img2020.cnblogs.com/blog/2300815/202102/2300815-20210221223618559-1361345336.png)
 mbeanClass)`
 * e.g.  接口是`com.cztruth.jmx.AppConfigMBean`, 则实现类则是`com.cztruth.jmx.AppConfig`
 */
public interface AppConfigMBean {
    /** A可读 */
    int getA();

    /** B可读 */
    String getB();

    /** B可写 */
    void setB(String newB);

    /** C可读 */
    int[] getC();

    /** C可写 */
    void setC(int[] newC);
}
  1. 实现MBean接口AppConfig.
package com.cztruth.jmx;


import java.util.Arrays;

/**
 *
 * 将本身实现的MBean注册到MBeanServer中须要知足如下两个条件之一:
 * jdk中描述是`implement DynamicMBean, or follows the Standard MBean conventions`
 * 1. 实现DynamicMBean接口.
 * 2. 自定义MBean接口,接口必须符合标准的MBean规范. 详见`com.sun.jmx.mbeanserver.Introspector.checkCompliance(Class<?> mbeanClass)`
 * e.g.`AppConfig`是`AppConfigMBean`的接口实现
 */
public class AppConfig implements AppConfigMBean {

    public int a;

    public String b;

    public int[] c;

    public AppConfig(int a, String b, int[] c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    @Override
    public int getA() {
        return this.a;
    }

    @Override
    public String getB() {
        return this.b;
    }

    @Override
    public void setB(String newB) {
        this.b = newB;
    }

    @Override
    public int[] getC() {
        return this.c;
    }

    @Override
    public void setC(int[] newC) {
        this.c = newC;
    }

    @Override
    public String toString() {
        return "AppConfig{" +
                "a=" + a +
                ", b='" + b + '\'' +
                ", c=" + Arrays.toString(c) +
                '}';
    }
}
  1. Main.java中将一个AppConfig对象注册到MBeanServer. 并用while让程序一直运行, 这样作使应用能够被远程链接和管理. 这里要注意ObjectName的name须要符合ObjectName的命名规范.
package com.cztruth.jmx;

import javax.management.*;
import java.lang.management.ManagementFactory;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {

    /**
     * 若是须要明确规定远程连接的端口, 须要在启动时加入如下jvm参数.
     * e.g. java -jar -Dcom.sun.management.jmxremote.port=3333 -D... -D... jmx.jar.
     * 若是使用的IDE工具启动项目须要去 Edit Configurations -> Configuration标签 -> VM options -> 添加 -D...
     * -Dcom.sun.management.jmxremote.port=3333
     * -Dcom.sun.management.jmxremote.ssl=false
     * -Dcom.sun.management.jmxremote.authenticate=false
     */
    public static void main(String[] args) {

        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ObjectName objectName = null;
        AppConfig appConfig = new AppConfig(1, "sout", new int[]{45,33});
        try {
            // ObjectName的名字不能乱取, 须要遵照ObjectName的规范。 详见: `javax.management.ObjectName`注释
            objectName = new ObjectName("com.cztruth.jmx.AppConfig:type=test,name=name1");
            mBeanServer.registerMBean(appConfig, objectName);
            /** 卡住线程, 而且每10s輸出一次 */
            while (true) {
                Thread.sleep(10000L);
                System.out.print(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "\t");
                System.out.println(appConfig);
            }
        } catch (MalformedObjectNameException e) {
            e.printStackTrace();
        } catch (NotCompliantMBeanException e) {
            e.printStackTrace();
        } catch (InstanceAlreadyExistsException e) {
            e.printStackTrace();
        } catch (MBeanRegistrationException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 将AppConfig对象从MBeanServer中注销
            if (objectName != null) {
                try {
                    mBeanServer.unregisterMBean(objectName);
                } catch (InstanceNotFoundException e) {
                    e.printStackTrace();
                } catch (MBeanRegistrationException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 使用jconsole监控管理MBean.

运行demo(Main.java 下的 main())app

找到并运行jconsole.exe. jconsole工具位于%JAVA_HOME%\bin\目录下. 博主的jconsole位于C:\Program Files\Java\jdk1.8.0_271\bin\jconsole.exe.jvm

选择本地进程中的com.cztruth.jmx.Main进程.

jconsole选择链接的进程

MBean的标签下咱们能够看到对应ObjectName(com.cztruth.jmx.AppConfig:type=test,name=name1). A, B, C三个属性值都是可读的.

jconsole管理MBean

经过jconsole, 修改该B的值为改好了, 而且控制台也正确输出了MBean对象的当前值已经被改动.

修改B

可是C属性却没法经过jconsole修改. 难道JMX没法管理数组? 理论上来讲AppConfig对象只是持有的C数组的引用, String类型的B能被替换, C应该也是能够的. 并且 <<深刻理解Java虚拟机>> 中提到过, 大部分bin目录下的工具都是jdk/lib/tools.jar的一层薄封装而已. 因此这里猜想jconsole只不过是没有将修改MBean数组属性的方式可视化操做. 以后查阅官方文档, 其中提到The JMX technology defines standard connectors (known as JMX connectors) that enable you to access JMX agents from remote management applications., 因此咱们能够经过JMX connectors去远程控制MBean.

  1. UpdateAppConfig.java中就实现了自定义远程监控管理MBean.

运行demo(Main.java 下的 main()), 而且在启动时加入VM options, 设置JMX远程占用的端口号.

若是运行的是jar包启动, 则在控制台输入java -jar -Dcom.sun.management.jmxremote.port=3333 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false jmx.jar启动.
若是是在IDEA中则在启动设置的VM options参数中加上三个-D....

添加VM options第一步

添加VM options第二步

new一个JMXServiceURL对象, 由于demo运行时 jmxremote port 设置的是3333, 因此url = service:jmx:rmi:///jndi/rmi://localhost:3333/jmxrmi. 而后经过Attribute对象设置一个新的C.

package com.cztruth.jmx;

import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Set;

public class UpdateAppConfig {

    public static void main(String[] args) {
        try {
            JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:3333/jmxrmi");
            JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL);
            MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
            // 查找全部的MBeans, 遍历就能获取全部的objectName
            Set<ObjectInstance> set = mBeanServerConnection.queryMBeans(null,null);
            for (ObjectInstance objectInstance : set) {
                System.out.println(objectInstance.getObjectName().toString());
            }
            // 这里仍是强制获取用做demo的 objectName
            ObjectName objectName = new ObjectName("com.cztruth.jmx.AppConfig:type=test,name=name1");
            // 修改 属性 C
            Attribute cAttribute = new Attribute("C", new int[]{2,3,4,6});
            mBeanServerConnection.setAttribute(objectName, cAttribute);
            System.out.println();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ReflectionException e) {
            e.printStackTrace();
        } catch (InstanceNotFoundException e) {
            e.printStackTrace();
        } catch (MalformedObjectNameException e) {
            e.printStackTrace();
        } catch (AttributeNotFoundException e) {
            e.printStackTrace();
        } catch (InvalidAttributeValueException e) {
            e.printStackTrace();
        } catch (MBeanException e) {
            e.printStackTrace();
        }

    }
}

成功修改了C.

远程连接JMX并修改数组成功

3、JMX在commons-pool2中的应用

在commons-pools中使用到了MXBean. 科普下 MXBean与MBean的区别

看完org.apache.commons.pool2.impl.GenericObjectPoolMXBean接口, 以及它的实现org.apache.commons.pool2.impl.GenericObjectPool, 推断出
它的做用就是监控pool的配置和pool中的对象. 接口中的Set<DefaultPooledObjectInfo> listAllObjects()方法就是为了监控pool中的对象.

咱们使用一个测试用例, 将代码运行起来, 并往pool中添加三个对象, 借走其中一个. 而后使用jconsole工具监控.

package org.apache.commons.pool2.impl;

import org.junit.Test;

/**
 * 当前测试须要在commons-pools的测试用力中的`rg.apache.commons.pool2.impl.TestGenericObjectPool`类完成.
 * 固然也能够本身实现一个简单的PooledObjectFactory类.
 */
public class MyTest {

    private TestGenericObjectPool.SimpleFactory simpleFactory = null;
    protected GenericObjectPool<String> genericObjectPool = null;

    @Test(timeout = 600000)
    public void testJmxRegistration() {
        try {
            simpleFactory = new TestGenericObjectPool.SimpleFactory();
            genericObjectPool = new GenericObjectPool<>(simpleFactory);
            // 添加三个对象
            genericObjectPool.addObject(); // "0"
            genericObjectPool.addObject(); // "1"
            genericObjectPool.addObject(); // "2"
            // 10ms后借走一个对象
            Thread.sleep(10L);
            genericObjectPool.borrowObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 不要让线程结束
        while (true) {
            try {
                Thread.sleep(10L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

jconsole中简单的GenericObjectPool属性展现

经过listAllObjects操做, 咱们能够监控pool中的对象.

pool中的对象1

能够看到池子中有三个对象, 而且一个对象被借走一次.

pool中的对象2

感谢阅读!

相关文章
相关标签/搜索