簡單來講JMX技術提供了一套輕量的標準化的資源管理方式. 什麼是資源的管理? 就是資源的增刪改查!java
使用JMX技術, 能夠管理和監控Java虛擬機的資源(內存, CPU, 線程等). 也能夠將符合規範的 Managed Bean(MBean) 對象註冊到MBean服務器, MBean服務器做爲JMX代理, 本地或者遠程控制MBean.chrome
JDK自帶的jconsole
工具就能夠監控管理java應用中的MBean. jconsole
工具位於%JAVA_HOME%\bin\
目錄下.apache
若是須要更準確更詳細的瞭解JMX技術, 強烈建議閱讀官方文檔. 英文看起來吃力能夠使用chrome瀏覽器, 右鍵翻譯成中文.數組
demo使用JMX實如今不重啓應用的狀況下, 動態修改對象的屬性值. 該方法能夠用於動態修改java應用中的配置.瀏覽器
demo目錄
服務器
將MBean對象註冊到MBeanServer中須要知足兩個條件之一: 實現DynamicMBean接口 或者 自定義符合標準MBean規範的接口. demo中咱們採用自定義MBean接口.oracle
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); }
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) + '}'; } }
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(); } } } } }
運行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
進程.
在MBean
的標籤下咱們能夠看到對應ObjectName(com.cztruth.jmx.AppConfig:type=test,name=name1). A, B, C三個屬性值都是可讀的.
經過jconsole, 修改該B的值爲改好了
, 而且控制檯也正確輸出了MBean對象的當前值已經被改動.
可是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.
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...
.
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.
在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(); } } } }
經過listAllObjects操做, 咱們能夠監控pool中的對象.
能夠看到池子中有三個對象, 而且一個對象被借走一次.