您的位置:首頁>正文

開源框架是如何通過JMX來做監控的(一)-JMX簡介和Standard MBean

開源框架是如何通過JMX來做監控的(二) - Druid連接池的監控

相信很多做Java開發的同學都使用過JDK自帶的 jconsole 或者 jvisualvm 監控過JVM的運行情況, 但不知道有沒有留意過它們會有一個MBean的功能/標籤, 通過MBean可以看到在JVM中運行的元件的一些屬性和操作

例如, 可以看到Tomcat 8080埠Connector的請求連接池資訊, Druid資料庫連接池的activeCount連接數以及連接池配置資訊, 這些開源框架或中介軟體都是通過JMX的方式將自己的一些管理和監控資訊暴露給我們

JMX的全稱為Java Management Extensions. 顧名思義, 是管理Java的一種擴展。 這種機制可以方便的管理正在運行中的Java程式。 常用於管理執行緒,

記憶體, 日誌Level, 服務重啟, 系統環境等。

下面簡單介紹一下JMX和其最簡單的Standard MBean, 以及常用實例, 之後的文章中會分析Druid資料庫連接池等開源框架是如何通過JMX做監控的, 沒准在我們的程式中也可以用到。

以下是本文的目錄大綱:

若有不正之處請多多諒解, 歡迎批評指正、互相討論。

一、JMX架構及基本概念

從上面的架構圖可以看到JMX主要分三層, 分別是:

1、設備層(Instrumentation Level)

主要定義了資訊模型。 在JMX中, 各種管理物件以管理構件的形式存在, 需要管理時, 向MBean伺服器進行註冊。 該層還定義了通知機制以及一些輔助中繼資料類。

設備層其實就是和被管設備通信的模組, 對於上層的管理者來說, Instrumentation 就是設備, 具體設備如何通信, 是採用SNMP,還是採用ICMP, 是MBean的事情。

該層定義了如何實現JMX管理資源的規範。 一個JMX管理資源可以是一個Java應用、一個服務或一個設備, 它們可以用Java開發, 或者至少能用Java進行包裝, 並且能被置入JMX框架中, 從而成為JMX的一個管理構件(Managed Bean), 簡稱MBean。 管理構件可以是標準的, 也可以是動態的, 標準的管理構件遵從JavaBeans構件的設計模式;動態的管理構件遵從特定的介面,

提供了更大的靈活性。

在JMX規範中, 管理構件定義如下:它是一個能代表管理資源的Java物件, 遵從一定的設計模式, 還需實現該規範定義的特定的介面。 該定義了保證了所有的管理構件以一種標準的方式來表示被管理資源。

管理介面就是被管理資源暴露出的一些資訊, 通過對這些資訊的修改就能控制被管理資源。 一個管理構件的管理介面包括:

1) 能被接觸的屬性值

2) 能夠執行的操作

3) 能發出的通知事件

4) 管理構件的構建器

本文著重介紹最基本也是用的最多的Standard Mbean, 至於其它的如Dynamic MBean、Model MBean暫時不介紹, 請參考本文最後資料中的文章。

Standard MBean是最簡單的MBean, 它管理的資源必須定義在介面中,

然後MBean必須實現這個介面。 它的命名也必須遵循一定的規範, 例如我們的MBean為Hello, 則介面必須為HelloMBean。

2、代理層(Agent Level)

Agent層 用來管理相應的資源, 並且為遠端使用者提供訪問的介面。 Agent層構建在設備層之上, 並且使用並管理設備層內部描述的元件。 Agent層主要定義了各種服務以及通信模型。 該層的核心是 MBeanServer, 所有的MBean都要向它註冊, 才能被管理。 註冊在MBeanServer上的MBean並不直接和遠端應用程式進行通信, 他們通過協議適配器(Adapter) 和連接器(Connector)進行通信。 通常Agent由一個MBeanServer和多個系統服務組成。 JMX Agent並不關心它所管理的資源是什麼。

3、分佈服務層(Distributed Service Level)

分佈服務層關心Agent如何被遠端用戶訪問的細節。 它定義了一系列用來訪問Agent的介面和元件, 包括Adapter和Connector的描述。

二、Standard MBean

Standard MBean的設計和實現是最簡單的,

它們的管理介面通過方法名來描述。 Standard MBean的實現依靠一組命名規則。 這些命名規則定義了屬性和操作。

一個唯讀屬性在MBean中只有get方法, 既有get又有set方法表示是一個可讀寫的屬性。

為了實現Standard MBean, 必須遵循一套繼承規範。 必須為每一個MBean定義一個介面, 而且這個介面的名字必須是其被管理的資源的物件類的名稱後面加上”MBean”, 之後把它們註冊到MBeanServer中就可以了

MBean介面:

public interface HelloMBean { public String getName; public void setName(String name); public String printHello; public String printHello(String whoName); }

接下來是真正的資源物件, 因為命名規範的限制, 因此物件名稱必須為Hello

public class Hello implements HelloMBean { private String name; @Override public String getName { return name; } @Override public void setName(String name) { this.name = name; } @Override public String printHello { return "Hello "+ name; } @Override public String printHello(String whoName) { return "Hello " + whoName; } }

接下來創建一個HelloMBean, 並將其註冊到MBeanServer:

public class HelloAgent { public static void main(String[] args) throws Exception { // 首先建立一個MBeanServer, MBeanServer用來管理我們的MBean, 通常是通過MBeanServer來獲取我們MBean的資訊 MBeanServer server = ManagementFactory.getPlatformMBeanServer; String domainName = "MyMBean"; // 為MBean(下面的new Hello())創建ObjectName實例 ObjectName helloName = new ObjectName(domainName+":name=HelloWorld"); // 將new Hello這個物件註冊到MBeanServer上去 server.registerMBean(new Hello,helloName); } }

三、通過RMI方式連接JMX Server

繼續上面的HelloMBean、HelloAgent, 我們可以通過RMI的方式註冊URL來提供用戶端連接, 這樣就可以通過 jvisualvm 或者 自己寫的Java程式作為JMX的用戶端來管理MBean

import java.lang.management.ManagementFactory; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; public class HelloAgent { public static void main(String[] args) throws Exception { //create mbean server MBeanServer server = ManagementFactory.getPlatformMBeanServer; //create object name ObjectName objectName = new ObjectName("jmxBean:name=hello"); //create mbean and register mbean server.registerMBean(new Hello, objectName); /** * JMXConnectorServer service */ //這句話非常重要,不能缺少!註冊一個埠,綁定url後,用戶端就可以使用rmi通過url方式來連接JMXConnectorServer Registry registry = LocateRegistry.createRegistry(1099); //構造JMXServiceURL JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"); //創建JMXConnectorServer JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, server); //啟動 cs.start; } }

在創建JMXConnectorServer時創建的JMXServiceURL比較複雜,但其實其完整版為:

service:jmx:rmi://localhost:0/jndi/rmi://localhost:1099/jmxrmi

藍色部分可以省略掉

service:jmx:這個是JMX URL的標準首碼,所有的JMX URL都必須以該字串開頭,否則會拋MalformedURLException

rmi:這個是jmx connector server的傳輸協議,在這個url中是使用rmi來進行傳輸的

localhost:0這個是jmx connector server的IP和埠,也就是真正提供服務的host和埠,可以忽略,那麼會在運行期間隨意綁定一個埠提供服務

jndi/rmi://localhost:1099/jmxrmi這個是jmx connector server的路徑,具體含義取決於前面的傳輸協議。比如該URL中這串字串就代表著該jmx connector server的stub是使用 jndi api 綁定在 rmi://localhost:1099/jmxrmi 這個位址

如果在伺服器端,我們用該URL創建一個jmx connector server,則大概流程如下:

1、將jmx connect server內部的server物件的rmi stub export到本地的一個隨機埠(也可以自己指定),接收外部連接

2、通過jndi api將該stub綁定在rmi://localhost:6000/jmxrmi這個位址上,這需要在本地的1099埠上運行著一個rmiregistry,如果不存在則會拋出異常

如果在用戶端,我們通過該URL創建一個connector,則大概流程如下:

1、訪問1099埠,通過jndi api到rmi://localhost:1099/jmxrmi這個位址取回stub

2、stub中已經包含了真實伺服器的IP和埠,所以可以直接根據該stub連接到真實的伺服器

下面具體展示一段用戶端代碼,可以獲取jmx connector,並展示HelloMBean的一些資訊,屬性,及調用其方法

import java.util.Iterator; import java.util.Set; import javax.management.Attribute; import javax.management.MBeanInfo; import javax.management.MBeanServerConnection; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; public class JMXClient { public static void main(String[] args) throws Exception { //connect JMX JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"); JMXConnector jmxc = JMXConnectorFactory.connect(url,null); MBeanServerConnection mbsc = jmxc.getMBeanServerConnection; ObjectName mbeanName = new ObjectName("jmxBean:name=hello"); //print domains System.out.println("Domains:---------------"); String domains = mbsc.getDomains; for (int i = 0; i

例子中我們可以get/set HelloMBean的Name屬性,可以通過先獲取代理和直接RMI的方式調用HelloMbean的printHello方法等。

這樣我們就有了一個完整的JMX server、client的例子,很多開源框架(如一些資料庫連接池DBCP2、Druid)都實現了JMX來達到對其自身的監控,雖然這不是唯一的方法,也可以通過其它方式提供監控查詢介面,但由於JMX是sun提出的通用標準,故大家紛紛回應實現。所以當我們使用這些開源框架並希望對其運行狀況做一些管理監控時,可以採用JMX的方式獲取其暴露出的MBean相關屬性和方法。之後會分析一些Druid是如何通過JMX的方式做管理監控的。

四、通過VisualVM連接JMX Server

打開JDK自帶的VisualVM,由於本例是本地localhost的JMX server,那麼在左側欄“本地”上右鍵,創建JMX連接

點擊後只需填寫本地的RMIRegistry註冊的埠1099即可,當然也可以填寫完整的URL:service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi

連接成功後,就可以看到HelloMBean以及其提供的屬性和操作,還可以調用printHello方法

這樣就可以通過 jvisualvm 或者 自己寫的Java程式作為JMX的用戶端來管理MBean

import java.lang.management.ManagementFactory; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; public class HelloAgent { public static void main(String[] args) throws Exception { //create mbean server MBeanServer server = ManagementFactory.getPlatformMBeanServer; //create object name ObjectName objectName = new ObjectName("jmxBean:name=hello"); //create mbean and register mbean server.registerMBean(new Hello, objectName); /** * JMXConnectorServer service */ //這句話非常重要,不能缺少!註冊一個埠,綁定url後,用戶端就可以使用rmi通過url方式來連接JMXConnectorServer Registry registry = LocateRegistry.createRegistry(1099); //構造JMXServiceURL JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"); //創建JMXConnectorServer JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, server); //啟動 cs.start; } }

在創建JMXConnectorServer時創建的JMXServiceURL比較複雜,但其實其完整版為:

service:jmx:rmi://localhost:0/jndi/rmi://localhost:1099/jmxrmi

藍色部分可以省略掉

service:jmx:這個是JMX URL的標準首碼,所有的JMX URL都必須以該字串開頭,否則會拋MalformedURLException

rmi:這個是jmx connector server的傳輸協議,在這個url中是使用rmi來進行傳輸的

localhost:0這個是jmx connector server的IP和埠,也就是真正提供服務的host和埠,可以忽略,那麼會在運行期間隨意綁定一個埠提供服務

jndi/rmi://localhost:1099/jmxrmi這個是jmx connector server的路徑,具體含義取決於前面的傳輸協議。比如該URL中這串字串就代表著該jmx connector server的stub是使用 jndi api 綁定在 rmi://localhost:1099/jmxrmi 這個位址

如果在伺服器端,我們用該URL創建一個jmx connector server,則大概流程如下:

1、將jmx connect server內部的server物件的rmi stub export到本地的一個隨機埠(也可以自己指定),接收外部連接

2、通過jndi api將該stub綁定在rmi://localhost:6000/jmxrmi這個位址上,這需要在本地的1099埠上運行著一個rmiregistry,如果不存在則會拋出異常

如果在用戶端,我們通過該URL創建一個connector,則大概流程如下:

1、訪問1099埠,通過jndi api到rmi://localhost:1099/jmxrmi這個位址取回stub

2、stub中已經包含了真實伺服器的IP和埠,所以可以直接根據該stub連接到真實的伺服器

下面具體展示一段用戶端代碼,可以獲取jmx connector,並展示HelloMBean的一些資訊,屬性,及調用其方法

import java.util.Iterator; import java.util.Set; import javax.management.Attribute; import javax.management.MBeanInfo; import javax.management.MBeanServerConnection; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; public class JMXClient { public static void main(String[] args) throws Exception { //connect JMX JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"); JMXConnector jmxc = JMXConnectorFactory.connect(url,null); MBeanServerConnection mbsc = jmxc.getMBeanServerConnection; ObjectName mbeanName = new ObjectName("jmxBean:name=hello"); //print domains System.out.println("Domains:---------------"); String domains = mbsc.getDomains; for (int i = 0; i

例子中我們可以get/set HelloMBean的Name屬性,可以通過先獲取代理和直接RMI的方式調用HelloMbean的printHello方法等。

這樣我們就有了一個完整的JMX server、client的例子,很多開源框架(如一些資料庫連接池DBCP2、Druid)都實現了JMX來達到對其自身的監控,雖然這不是唯一的方法,也可以通過其它方式提供監控查詢介面,但由於JMX是sun提出的通用標準,故大家紛紛回應實現。所以當我們使用這些開源框架並希望對其運行狀況做一些管理監控時,可以採用JMX的方式獲取其暴露出的MBean相關屬性和方法。之後會分析一些Druid是如何通過JMX的方式做管理監控的。

四、通過VisualVM連接JMX Server

打開JDK自帶的VisualVM,由於本例是本地localhost的JMX server,那麼在左側欄“本地”上右鍵,創建JMX連接

點擊後只需填寫本地的RMIRegistry註冊的埠1099即可,當然也可以填寫完整的URL:service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi

連接成功後,就可以看到HelloMBean以及其提供的屬性和操作,還可以調用printHello方法

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示