Java 調式、熱部署、JVM 背後的支持者 Java Agent

若是你以前沒接觸過,必定會出現疑問三連擊,"這是個什麼玩意兒?幹嗎的?有啥用?"。java

雖然可能不知道它,可是或多或少你確定都接觸過。web

好比你若是用 Spring Boot,那你用過 Spring Boot Actuator 吧,它就用到了 JMX 。好比你用過 JConsole 或者 VisualVM 吧,它們也用到了 JMX。瀏覽器

下圖是 JConsole 和 VisualVM 的界面。架構

圖片描述

它們是如何用到了 JMX 呢,下面一步一步說。框架

什麼是 JMX

JMX 全稱爲 Java Management Extensions,翻譯過來就是 Java 管理擴展,用來管理和監測 Java 程序。最經常使用到的就是對於 JVM 的監測和管理,好比 JVM 內存、CPU 使用率、線程數、垃圾收集狀況等等。另外,還能夠用做日誌級別的動態修改,好比 log4j 就支持 JMX 方式動態修改線上服務的日誌級別。最主要的仍是被用來作各類監控工具,好比文章開頭提到的 Spring Boot Actuator、JConsole、VisualVM 等。dom

JMX 既是 Java 管理系統的一個標準,一個規範,也是一個接口,一個框架。有標準、有規範是爲了讓開發者能夠定製開發本身的擴展功能,並且做爲一個框架來說,JDK 已經幫咱們實現了經常使用的功能,尤爲是對 JVM 的監控和管理。ide

圖片描述
上圖是 JMX 架構的簡單示意圖,簡單理解就是管理系統經過 JMX 能夠管理各類資源。工具

管理系統能夠理解爲管理客戶端,好比上面說的 JConsole、VisualVM ,還有 Metrics 這個很是知名的 Java 監控工具包,或者你本身經過 JMX 接口實現的客戶端等。
各類資源好比系統配置、JVM 指標等,或者你本身的項目中特定的資源等。url

重點來了,JMXspa

圖片描述

這個架構圖是把上面的架構示意圖展開了,下面從底向上簡單介紹一下。

MBean

JMX 是經過各類 MBean(Managed Bean) 傳遞消息的,MBean 其實就是咱們常常說的 Java Bean,只不過因爲它比較特殊,因此稱之爲 MBean。既然是個 Bean,裏面就是一些屬性和方法,外界就能夠獲取被管理的資源的狀態和操縱MBean的行爲。JMX 中共有四種類型的 MBean,分別是 Standard MBean, Dynamic MBean, Open MBean, Model MBean。JDK 提供的 MBean 主要在 java.lang.managementjavax.management包裏面。能夠進去看一看,進去就能看到好多似曾相識的身影,好比 Memory 相關的、Thread 相關的,這不就是咱們在 VisualVM 上看到的內容嗎,沒錯,數據就是從這裏來的。

說實話,並不用太關心這幾種 MBean 的區別,但仍是簡單介紹下。

Standard MBean 就是普通的 Java Bean 沒有區別,它也是 JMX 中最簡單、使用最多的一種。主要在java.lang.management包裏面。

Dynamic MBean 實際上是一種妥協的產物,因爲已經存在一些這種 MBean,而將其改形成標準 MBean 比較費力並且不切實際,因此就有了動態 MBean。Dynamic MBean 的接口在 javax.management.DynamicMBean這裏,裏面定義一些接口方法,好比動態獲取屬性、設置屬性等。

另外還有兩類 MBean:Open MBean 和 Model MBean,實際上它們也都是動態 MBean。

Open MBean 與其它動態 MBean 的惟一區別在於,前者對其公開接口的參數和返回值有所限制 —— 只能是基本類型或者 javax.management.openmbean包內的 ArrayType、CompositeType、TarbularType 等類型。這主要是考慮到管理系統的分佈,極可能遠端管理系統甚至 MBServer 層都不具備 MBean 接口中特殊的類。

MBeanServer

MBeanServer 是負責管理 MBean 的,通常一個 JVM 只有一個 MBeanServer,全部的 MBean 都要註冊到 MBeanServer 上,並經過 MBeanServer 對外提供服務。通常用 ManagementFactory.getPlatformMBeanServer()方法獲取當前 JVM 內的 MBeanServer。

適配器和鏈接器

寫好的 MBean 註冊到 MBeanServer 上以後,功能已經具有了。適配器和鏈接器就是將這些功能開放出來的方式。
好比 HTTP協議適配器,就是將功能以 HTTP 協議開放出去,這樣咱們就能夠在瀏覽器使用了。可是 JDK 只是提供了適配器的實現標準,並無具體的實現,比較經常使用的是 HtmlAdaptorServer,須要 jmxtools.jar 包的支持。

鏈接器是各類客戶端最經常使用的,JDK 提供的默認鏈接器是 RMI 鏈接器,JConsole、VisualVM 都是使用它。

實現並使用一個 MBean

雖然 Java 提供了實現 MBean 的標準和規則,但平時咱們幾乎不須要開發 MBean。絕大多數的開發者接觸到的也僅僅是使用 JDK 或者第三方定義好的 MBean,即使是第三方有實現 MBean,也是很是少的。咱們知道的有 Tomcat 和 Spring Boot Actuator。

定義 MBean 接口 和實體類

// 接口
public interface UserMBean {

    String getName();

    String getPassword();

    String getPhone();

    void say();
}
public class User implements UserMBean {

    @Override
    public String getName() {
        return "風箏";
    }

    @Override
    public String getPassword() {
        return "密碼不可見";
    }

    @Override
    public String getPhone() {
        return "18900000000";
    }

    @Override
    public void say() {
        System.out.println("Hello JMX");
    }
}

實體類須要繼承 MBean 接口類,而接口類的命名規則是 「實體類名+MBean」,這是固定的規則。

將定義好的 MBean 註冊到 MBeanServer

public static void main(String[] args) throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName userName = new ObjectName("FengZheng:type=customer,name=customerUserBean");
        server.registerMBean(new User(), userName);
  
        try {
            //這個步驟很重要,註冊一個端口,綁定url後用於客戶端經過rmi方式鏈接JMXConnectorServer
            LocateRegistry.createRegistry(8999);
            //URL路徑的結尾能夠隨意指定,但若是須要用Jconsole來進行鏈接,則必須使用jmxrmi
            JMXServiceURL url = new JMXServiceURL
                    ("service:jmx:rmi:///jndi/rmi://localhost:8999/jmxrmi");
            JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
            System.out.println("begin rmi start");
            jcs.start();
            System.out.println("rmi start");
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Thread.sleep(60 * 60 * 1000);
}

ManagementFactory.getPlatformMBeanServer()獲取當前 JVM 的 MBeanServer,ObjectName 是 MBean 的惟一標示,一個 MBeanServer 不能有重複。完整的格式「自定義命名空間:type=自定義類型,name=自定義名稱」。固然你能夠只聲明 type ,不聲明 name。

使用 JConsole 查看

JConsole 是 JDK 自帶的工具,在${JAVA_HOME}的 bin 目錄下,啓動便可。啓動後在本地進程找到上一步啓動的 main 方法所在的進程。

圖片描述

在 JConsole 上方有內存、線程、類等選項卡,點擊最後一個 MBean,經過這個選項卡能夠看到當前 JVM 全部已定義的 MBean。能夠看到系統定義的 MBean ,以及剛剛咱們定義的這個 MBean。

圖片描述

點擊屬性能夠在右側查看屬性值,而且在操做菜單項下,能夠看到咱們定義的方法,而且能夠調用。

圖片描述

圖片描述

使用 RMI 方式鏈接

RMI 通常是用來鏈接遠程服務的,固然本地進程也能夠。這也是實現鏈接遠程服務客戶端的第一步。咱們在註冊 MBean 的時候,有沒有注意到註冊完成後,還有一大段代碼,那段代碼就是用來開啓 RMI 鏈接的,開啓 8999 端口做爲 RMI 訪問端口,而後客戶端就能夠用固定的鏈接串鏈接了。

鏈接串的格式 service:jmx:rmi:///jndi/rmi://host:port/jmxrmi

public class Client {
    public static void main(String[] args) throws IOException, Exception, NullPointerException {
        String jmxUrl = "service:jmx:rmi:///jndi/rmi://localhost:8999/jmxrmi";
        monitor(jmxUrl);
    }

    public static void monitor(String url) throws Exception{
        JMXServiceURL jmxServiceURL = new JMXServiceURL
                (url);
        JMXConnector jmxc = JMXConnectorFactory.connect(jmxServiceURL, null);

        MBeanServerConnection msc = jmxc.getMBeanServerConnection();
        String[] domains = msc.getDomains();
        for (String domain : domains) {
            System.out.println(domain);
        }
    }
}

首先根據鏈接串得到一個 JMXServiceURL對象,以後經過JMXConnectorFactory.connect方法獲取 JMXConnector,再經過 getMBeanServerConnection拿到 MBeanServerConnection。以後就能夠獲取全部的 MBean 了。其中 getDomains是獲取全部命名空間的方法,也就是咱們上面定義的 "FengZheng",以及 JMImplementation、java.lang 等,咱們在 JConsole 看到的就是這些。

哎呀,好累,不想寫了,原本還想介紹系統定義的 JVM 相關的 MBean 以及具體的操做 MBean 的方式,還有我寫這個的時候本身寫的一個 web 版的客戶端,功能和 JConsole 、VisualVM 基本相同,也支持本地和遠程鏈接。下一篇一塊介紹吧,先放兩張截圖。

圖片描述

支持遠程和本地鏈接

圖片描述

查看全部的 MBean ,以及 MBean 的屬性,而且能夠調用方法。

圖片描述

動態查看 CPU、堆、metasapce(Java1.8)、類、線程等

更多幹貨請關注公衆號:古時的風箏
圖片描述

相關文章
相關標籤/搜索