開源框架是如何通過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 < domains.length; i++) {
System.out.println("Domain[" + i +"] = " + domains[i]);
}
System.out.println;
//MBean count
System.out.println("MBean count:---------------");
System.out.println("MBean count = " + mbsc.getMBeanCount);
System.out.println;
//process attribute
System.out.println("process attribute:---------------");
mbsc.setAttribute(mbeanName, new Attribute("Name", "newName")); //set value
System.out.println("Name = " + mbsc.getAttribute(mbeanName, "Name")); //get value
System.out.println;
//invoke via proxy
System.out.println("invoke via proxy:---------------");
HelloMBean proxy = (HelloMBean) MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, HelloMBean.class, false);
System.out.println(proxy.printHello);
System.out.println(proxy.printHello("zhangsan"));
System.out.println;
//invoke via rmi
System.out.println("invoke via rmi:---------------");
System.out.println(mbsc.invoke(mbeanName, "printHello", null, null));
System.out.println(mbsc.invoke(mbeanName, "printHello", new Object { "lisi" }, new String { String.class.getName }));
System.out.println;
//get mbean information
System.out.println("get mbean information:---------------");
MBeanInfo info = mbsc.getMBeanInfo(mbeanName);
System.out.println("Hello Class:" + info.getClassName);
System.out.println("Hello Attribute:" + info.getAttributes[0].getName);
System.out.println("Hello Operation:" + info.getOperations[0].getName);
System.out.println;
//ObjectName of MBean
System.out.println("ObjectName of MBean:---------------");
Set set = mbsc.queryMBeans(null, null);
for (Iterator it = set.iterator; it.hasNext;) {
ObjectInstance oi = (ObjectInstance)it.next;
System.out.println(oi.getObjectName);
}
jmxc.close;
}
}
例子中我們可以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方法
TAG:科技優家 |
※谷歌開源AdaNet:基於TensorFlow的AutoML框架
※JoomScan:一款開源的OWASP Joomla漏洞掃描器
※領英開源TonY:構建在Hadoop YARN上的TensorFlow框架
※開源資料庫 PostgreSQL、MariaDB 和 SQLite 的對比
※資源 | 谷歌開源AdaNet:基於TensorFlow的AutoML框架
※LinkedIn開源TonY:在Hadoop上運行TensorFlow的框架
※LinkedIn 開源 TonY:在 Hadoop 上運行 TensorFlow 的框架
※資源 | 領英開源TonY:構建在Hadoop YARN上的TensorFlow框架
※TensorFlow、MXNet、PaddlePaddle三個開源庫對比
※高可用負載均衡:開源PaaS Rainbond組件Rainbond-Entrance揭秘
※「圖」開源的MobileShell:能讓你的PC變身大號Windows Phone
※微軟開源 Windows UX 框架:WPF、WinUI、Windows Forms
※AWS 對開源豎中指:推出 MongoDB 替代品 DocumentDB
※Facebook開源Linux內核組件和工具:BPF、Btrfs、Netconsd、Cgroup2、PSI、Oomd
※PSVita開源PC模擬器Vita3K:支持Win/Mac系統
※搭建Yearning 基於Inception的開源SQL審核平台
※Oracle開源GraphPipe:幾行代碼讓你在TensorFlow部署PyTorch模型
※入門級的開源CANopen協議棧——MicroCANopen
※蘋果 FoundationDB 開源 CloudKit 使用的數據層
※Facebook Mask R-CNN2Go已開源