1. 程式人生 > >【commons-pool2原始碼】_pre JMX

【commons-pool2原始碼】_pre JMX

## 目錄 - 一、定義 - 二、demo - 三、JMX在commons-pool2中的應用 ## 一、定義 > [JMX(Java Management Extensions)](https://docs.oracle.com/javase/tutorial/jmx/overview/index.html) 簡單來說JMX技術提供了一套輕量的標準化的**資源管理**方式. 什麼是資源的管理? 就是資源的增刪改查! 使用JMX技術, 可以管理和監控Java虛擬機器的資源(記憶體, CPU, 執行緒等). 也可以將**符合規範**的 Managed Bean(MBean) 物件註冊到MBean伺服器, MBean伺服器作為JMX代理, 本地或者遠端控制MBean. JDK自帶的`jconsole`工具就可以監控管理java應用中的MBean. `jconsole`工具位於`%JAVA_HOME%\bin\`目錄下. 如果需要更準確更詳細的瞭解JMX技術, 強烈建議閱讀[官方文件](https://docs.oracle.com/javase/9/jmx/toc.htm). 英文看起來吃力可以使用chrome瀏覽器, 右鍵翻譯成中文. ## 二、demo > demo使用JMX實現在不重啟應用的情況下, 動態修改物件的屬性值. 該方法可以用於動態修改java應用中的配置. demo目錄 ![demo目錄](https://img2020.cnblogs.com/blog/2300815/202102/2300815-20210221223721934-302079094.png) 將MBean物件註冊到MBeanServer中需要滿足兩個條件之一: 實現**DynamicMBean**介面 或者 自定義**符合標準MBean規範**的介面. demo中我們採用**自定義**MBean介面. 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); } ``` 2. 實現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) + '}'; } } ``` 3. `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(); } } } } } ``` 4. 使用jconsole監控管理MBean. 執行demo(Main.java 下的 main()) 找到並執行`jconsole.exe`. jconsole工具位於`%JAVA_HOME%\bin\`目錄下. 博主的jconsole位於`C:\Program Files\Java\jdk1.8.0_271\bin\jconsole.exe`. 選擇`本地程序`中的`com.cztruth.jmx.Main`程序. ![jconsole選擇連線的程序](https://img2020.cnblogs.com/blog/2300815/202102/2300815-20210221223753576-1278613527.png) 在`MBean`的標籤下我們可以看到對應ObjectName(com.cztruth.jmx.AppConfig:type=test,name=name1). A, B, C三個屬性值都是可讀的. ![jconsole管理MBean](https://img2020.cnblogs.com/blog/2300815/202102/2300815-20210221223816285-1527915687.png) 通過jconsole, 修改該B的值為`改好了`, 並且控制檯也正確輸出了MBean物件的當前值已經被改動. ![修改B](https://img2020.cnblogs.com/blog/2300815/202102/2300815-20210221223835182-1434656808.png) 但是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. 5. `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第一步](https://img2020.cnblogs.com/blog/2300815/202102/2300815-20210221223856270-1565492071.png) ![新增VM options第二步](https://img2020.cnblogs.com/blog/2300815/202102/2300815-20210221223908436-648446456.png) 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