【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

感謝閱讀!

相關文章
相關標籤/搜索