相關文章目錄:html
開源框架是如何經過JMX來作監控的(一) - JMX簡介和Standard MBeanjava
開源框架是如何經過JMX來作監控的(二) - Druid鏈接池的監控數據庫
相信不少作Java開發的同窗都使用過JDK自帶的 jconsole 或者 jvisualvm 監控過JVM的運行狀況,但不知道有沒有留意過它們會有一個MBean的功能/標籤,經過MBean能夠看到在JVM中運行的組件的一些屬性和操做設計模式
例如,能夠看到Tomcat 8080端口Connector的請求鏈接池信息,Druid數據庫鏈接池的activeCount鏈接數以及鏈接池配置信息,這些開源框架或中間件都是經過JMX的方式將本身的一些管理和監控信息暴露給咱們api
JMX的全稱爲Java Management Extensions. 顧名思義,是管理Java的一種擴展。這種機制能夠方便的管理正在運行中的Java程序。經常使用於管理線程,內存,日誌Level,服務重啓,系統環境等。服務器
下面簡單介紹一下JMX和其最簡單的Standard MBean,以及經常使用實例,以後的文章中會分析Druid數據庫鏈接池等開源框架是如何經過JMX作監控的,沒準在咱們的程序中也能夠用到。架構
如下是本文的目錄大綱:框架
1、JMX架構及基本概念dom
如有不正之處請多多諒解,歡迎批評指正、互相討論。
請尊重做者勞動成果,轉載請標明原文連接:
http://www.cnblogs.com/trust-freedom/p/6842332.html
從上面的架構圖能夠看到JMX主要分三層,分別是:
一、設備層(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。
二、代理層(Agent Level)
Agent層 用來管理相應的資源,而且爲遠端用戶提供訪問的接口。Agent層構建在設備層之上,而且使用並管理設備層內部描述的組件。Agent層主要定義了各類服務以及通訊模型。該層的核心是 MBeanServer,全部的MBean都要向它註冊,才能被管理。註冊在MBeanServer上的MBean並不直接和遠程應用程序進行通訊,他們經過 協議適配器(Adapter) 和 鏈接器(Connector) 進行通訊。一般Agent由一個MBeanServer和多個系統服務組成。JMX Agent並不關心它所管理的資源是什麼。
三、分佈服務層(Distributed Service Level)
分佈服務層關心Agent如何被遠端用戶訪問的細節。它定義了一系列用來訪問Agent的接口和組件,包括Adapter和Connector的描述。
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); } }
繼續上面的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,則大概流程以下: 一、將jmx connect server內部的server對象的rmi stub export到本地的一個隨機端口(也能夠本身指定),接收外部鏈接 二、經過jndi api將該stub綁定在rmi://localhost:6000/jmxrmi這個地址上,這須要在本地的1099端口上運行着一個rmiregistry,若是不存在則會拋出異常 |
若是在客戶端,咱們經過該URL建立一個connector,則大概流程以下: 一、訪問1099端口,經過jndi api到rmi://localhost:1099/jmxrmi這個地址取回stub 二、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的例子,不少開源框架(如一些數據庫鏈接池DBCP二、Druid)都實現了JMX來達到對其自身的監控,雖然這不是惟一的方法,也能夠經過其它方式提供監控查詢接口,但因爲JMX是sun提出的通用標準,故你們紛紛響應實現。因此當咱們使用這些開源框架並但願對其運行情況作一些管理監控時,能夠採用JMX的方式獲取其暴露出的MBean相關屬性和方法。以後會分析一些Druid是如何經過JMX的方式作管理監控的。
打開JDK自帶的VisualVM,因爲本例是本地localhost的JMX server,那麼在左側欄「本地」上右鍵,建立JMX鏈接
點擊後只需填寫本地的RMIRegistry註冊的端口1099便可,固然也能夠填寫完整的URL:service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
鏈接成功後,就能夠看到HelloMBean以及其提供的屬性和操做,還能夠調用printHello()方法
參考資料: