Java 監控基礎 - 使用 JMX 監控和管理 Java 程式

程式猿阿朗發表於2021-12-09

點贊再看,動力無限。Hello world : ) 微信搜「 程式猿阿朗 」。

本文 Github.com/niumoo/JavaNotes未讀程式碼網站 已經收錄,有很多知識點和系列文章。

此篇文章介紹 Java JMX 技術的相關概念和具體的使用方式。

當前文章屬於Java 效能分析優化系列文章,點選可以檢視所有文章。

系列連結:https://www.wdbyte.com/java/performance.html

  1. Arthas - Java 線上問題定位處理的終極利器
  2. 使用 JMH 進行 Java 基準測試
  3. Java 中的監控與管理原理概述
  4. 使用 JMX 監控和管理 Java 程式

1. JMX 是什麼?

Java Management Extensions(JMX)技術是 Java SE 平臺的標準功能,提供了一種簡單的、標準的監控和管理資源的方式,對於如何定義一個資源給出了明確的結構和設計模式,主要用於監控和管理 Java 應用程式執行狀態、裝置和資源資訊、Java 虛擬機器執行情況等資訊。 JMX 是可以動態的,所以也可以在資源建立、安裝、實現時進行動態監控和管理,JDK 自帶的 jconsole 就是使用 JMX 技術實現的監控工具。

使用 JMX 技術時,通過定義一個被稱為 MBeanMXBean 的 Java 物件來表示要管理指定的資源,然後可以把資源資訊註冊到 MBean Server 對外提供服務。MBean Server 充當了對外提供服務和對內管理 MBean 資源的代理功能,如此優雅的設計讓 MBean 資源管理和 MBean Server 代理完全獨立開,使之可以自由的控制 MBean 資源資訊。

JMX 不僅僅用於本地管理,JMX Remote API 為 JMX 新增了遠端功能,使之可以通過網路遠端監視和管理應用程式。

2. 為什麼使用 JMX 技術?

JMX 技術為 Java 開發者提供了一種簡單、靈活、標準的方式來監測 Java 應用程式,得益於相對獨立的架構設計,使 JMX 可以平滑的整合到各種監控系統之中。

下面列舉幾項 JMX 的具體優點:

  1. 開箱即用的監控功能,JMX 是 Java SE 的標準部分,提供了資源管理、服務託管、遠端監控等管理基礎功能,都可以直接啟用。
  2. JMX 技術提供了一種通用的、標準的資源、系統、應用程式、網路的管理方式,不僅可以本地使用、遠端使用;還可以擴充套件到其他場景,如 Java EE 應用等。
  3. JMX 技術提供了對 JVM 狀態的監測功能,JMX 已經內建了對 JVM 的監測功能,並且可以監控和管理 JVM,十分方便。
  4. JMX 架構設計優秀,元件化的設計可以自由的擴充套件。
  5. JMX 技術嚴格遵守 Java 現有規範如 JNDI 規範。
  6. JMX 可以自由的與其他管理解決方案整合,得益於開放的 JMX API,可以通過 web 服務管理 JMX 中的資源。

3. JMX 的技術架構

JMX 技術架構主要有資源管理(MBean/MXBean)模組,資源代理模組(MBean Server),遠端管理模組(Remote API)組成 ,下面的圖片來自維基百科,很好的展示了三個模組之間的關係。

圖片來自維基百科

3.1. 資源管理 MBean

資源管理在架構中標識為資源探測層(Probe Level),在 JMX 中, 使用 MBeanMXBean 來表示一個資源(下面簡稱 MBean),訪問和管理資源也都是通過 MBean,所以 MBean 往往包含著資源的屬性和操作方法

JMX 已經對 JVM 進行了多維度資源檢測,所以可以輕鬆啟動 JMX 代理來訪問內建的 JVM 資源檢測,從而通過 JMX 技術遠端監控和管理 JVM。

下面列舉 JMX 對 JVM 的資源檢測類,都可以直接使用。

資源介面 管理的資源 Object Name VM 中的例項個數
ClassLoadingMXBean 類載入 java.lang:type= ClassLoading 1個
CompilationMXBean 彙編系統 java.lang:type= Compilation 0 個或1個
GarbageCollectorMXBean 垃圾收集 java.lang:type= GarbageCollector, name=collectorName 1個或更多
LoggingMXBean 日誌系統 java.util.logging:type =Logging 1個
MemoryManagerMXBean 記憶體池 java.lang: typeMemoryManager, name=managerName 1個或更多
MemoryPoolMXBean 記憶體 java.lang: type= MemoryPool, name=poolName 1個或更多
MemoryMXBean 記憶體系統 java.lang:type= Memory 1個
OperatingSystemMXBean 作業系統 java.lang:type= OperatingSystem 1個
RuntimeMXBean 執行時系統 java.lang:type= Runtime 1個
ThreadMXBean 執行緒系統 java.lang:type= Threading 1個

下面的程式碼示例演示了使用 JMX 檢測 JVM 某些資訊的程式碼示例。

package com.wdbyte.jmx;

import java.lang.management.CompilationMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryManagerMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.util.List;
import java.util.stream.Collectors;

/**
 * JMX JVM
 *
 * @author https://www.wdbyte.com
 */
public class JavaManagementExtensions {

    public static void main(String[] args) {
        OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
        String osName = operatingSystemMXBean.getName();
        String osVersion = operatingSystemMXBean.getVersion();
        int processors = operatingSystemMXBean.getAvailableProcessors();
        System.out.println(String.format("作業系統:%s,版本:%s,處理器:%d 個", osName, osVersion, processors));

        CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean();
        String compilationMXBeanName = compilationMXBean.getName();
        System.out.println("編譯系統:" + compilationMXBeanName);

        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
        long max = heapMemoryUsage.getMax();
        long used = heapMemoryUsage.getUsed();
        System.out.println(String.format("使用記憶體:%dMB/%dMB", used / 1024 / 1024, max / 1024 / 1024));

        List<GarbageCollectorMXBean> gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
        String gcNames = gcMXBeans.stream()
            .map(MemoryManagerMXBean::getName)
            .collect(Collectors.joining(","));
        System.out.println("垃圾收集器:" + gcNames);
    }
}

執行可以得到如下結果:

作業系統:Mac OS X,版本:11.6,處理器:12 個
編譯系統:HotSpot 64-Bit Tiered Compilers
使用記憶體:3MB/4096MB
垃圾收集器:G1 Young Generation,G1 Old Generation

3.2. 資源代理 MBean Server

資源代理 MBean Server 是 MBean 資源的代理,通過 MBean Server 可以讓 MBean 資源用於遠端管理, MBean 資源和 MBean Server 往往都是在同一個 JVM 中,但這不是必須的。

想要 MBean Server 可以管理 MBean 資源,首先要把資源註冊到 MBean Server,任何符合 JMX 的 MBean 資源都可以進行註冊,最後 MBean Server 會暴露一個遠端通訊介面對外提供服務。

3.3. JMX 遠端管理

可以通過網路協議訪問 JMX API,如 HTTP 協議、SNMP(網路管理協議)協議、RMI 遠端呼叫協議等,JMX 技術預設實現了 RMI 遠端呼叫協議。

受益於資源管理 MBean 的充分解耦,可以輕鬆的把資源管理功能擴充套件到其他協議,如通過 HTTP 在網頁端進行管理。

4. JMX 的具體使用

在資源管理 MBean 部分已經演示了使用 JMX 獲取 JVM 執行資訊,那麼如果想要自定義一個資源 MBean 呢?

下面通過一個例子,模擬一個記憶體資源 MBean,最後對它進行遠端管理。

4.1. 編寫資源管理 MBean

MBean 的編寫必須遵守 JMX 的設計規範,MBean 很像一個特殊的 Java Bean,它需要一個介面和一個實現類。MBean 資源介面總是以 MBean 或者 MXBean 結尾實現類則要以介面去掉 MBean 或 MXBean 之後的名字來命名

編寫一個記憶體資源管理 MBean 介面,定義如下:

package com.wdbyte.jmx;

/**
 * @author https://www.wdbyte.com
 */
public interface MyMemoryMBean {

    long getTotal();

    void setTotal(long total);

    long getUsed();

    void setUsed(long used);

    String doMemoryInfo();
}

然後實現這個介面:

package com.wdbyte.jmx;

/**
 * @author https://www.wdbyte.com
 */
public class MyMemory implements MyMemoryMBean {

    private long total;
    private long used;

    @Override
    public long getTotal() {
        return total;
    }

    @Override
    public void setTotal(long total) {
        this.total = total;
    }

    @Override
    public long getUsed() {
        return used;
    }

    @Override
    public void setUsed(long used) {
        this.used = used;
    }

    @Override
    public String doMemoryInfo() {
        return String.format("使用記憶體: %dMB/%dMB", used, total);
    }

}

這個例子在 MyMemory.java 中只有兩個 long 基本型別屬性,所以介面是以 MBean 結尾。如果資源實現類中的屬性是自定義實體類的引用,那麼介面就需要以 MXBean 結尾。

這樣就完成了執行緒數量資源 MBean 的建立,其中 totalused 是資源屬性,doMemoryInfo 是資源操作方法。

4.2. 註冊資源到 MBean Server

通過上面的 JMX 架構圖,我們知道 MBean 資源需要註冊到 MBean Server 進行代理才可以暴露給外部進行呼叫,所以我們想要通過遠端管理我們自定義的 MyMemory 資源,需要先進行資源代理。

package com.wdbyte.jmx;

import java.lang.management.ManagementFactory;

import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;

/**
 * @author https://www.wdbyte.com
 */
public class MyMemoryManagement {

    public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException,
        InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException {
        // 獲取 MBean Server
        MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
        MyMemory myMemory = new MyMemory();
        myMemory.setTotal(100L);
        myMemory.setUsed(20L);
        // 註冊
        ObjectName objectName = new ObjectName("com.wdbyte.jmx:type=myMemory");
        platformMBeanServer.registerMBean(myMemory, objectName);

        while (true) {
            // 防止進行退出
            Thread.sleep(3000);
            System.out.println(myMemory.doMemoryInfo());
        }
    }
}

啟動後可以看到控制檯每隔三秒列印我們自定義的記憶體資訊。

使用記憶體: 20MB/100MB
使用記憶體: 20MB/100MB

不加任何 JVM 引數啟動 Java 程式,JMX 只能在當前機器訪問,如果想要通過網路在真正的遠端訪問,那麼在啟動時需要指定當前機器 ip 和開放的埠。

$ java -Dcom.sun.management.jmxremote=true \  # 開啟遠端訪問
-Dcom.sun.management.jmxremote.port=8398 \		# 自定義 JMX 埠
-Dcom.sun.management.jmxremote.ssl=false \		# 是否使用 SSL 協議,生產環境一定要開啟
-Dcom.sun.management.jmxremote.authenticate=false \ # 是否需要認證,生產環境一定要開啟
-Djava.rmi.server.hostname=150.158.2.56 YourClass.java # 當前機器 ip

4.3. 遠端管理 jconsole

jconsole 是 Java 自帶的基於 JMX 技術的監控管理工具,如果已經配置了 JDK 環境變數,可以直接控制檯通過 jconsole 命令啟動。

啟動 jconsole 後會列出當前機器上的 Java 進行,這裡選擇自己要監控的 Java 程式進行監控,連線後會提示不安全的協議,是因為 Java 程式預設啟動是不會配置 HTTPS 協議的原因。

連線後可以看到多維度的 JVM 監控資訊,這些資訊都是通過讀取 JVM 資源 MBean 資訊得到的。

在下面這個頁面列舉了執行緒資訊,注意最下面的執行緒資訊,可以看到 RMI TCP 執行緒,這裡也證明了 JMX 預設通過 RMI 協議進行遠端管理。

在 MBean 頁面可以瀏覽所有可管理的 MBean 資訊,也可以看到我們自定義的 com.wdbyte.jmx 中的記憶體資訊,甚至可以直接修改其中的 used 變數。

修改後控制檯日誌立即發生變化,可以看到已經修改成功。

使用記憶體: 20MB/100MB
使用記憶體: 20MB/100MB
使用記憶體: 20MB/100MB
使用記憶體: 30MB/100MB

在操作中可以呼叫 doMemoryInfo 方法,呼叫後可以看到返回值中使用記憶體已經由啟動時的 20MB 更新為 30MB。

一如既往,當前文章中的程式碼示例都存放在 github.com/niumoo/JavaNotes.

當前系列:

  1. Arthas - Java 線上問題定位處理的終極利器
  2. 使用 JMH 進行 Java 基準測試
  3. Java 中的監控與管理原理概述
  4. 使用 JMX 監控和管理 Java 程式

參考:

<完>

Hello world : ) 我是阿朗,一線技術工具人,認認真真寫文章,求個點贊。

文章持續更新,可以關注公眾號「 程式猿阿朗 」或訪問未讀程式碼部落格(https://www.wdbyte.com) 」。

本文 Github.com/niumoo/JavaNotes 已經收錄,有很多知識點和系列文章,歡迎Star。

等你好久

相關文章