【commons-pool2原始碼】_pre JMX
阿新 • • 發佈:2021-02-22
## 目錄
- 一、定義
- 二、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