JMX最常見的場景是監(jiān)控Java程序的基本信息和運(yùn)行情況,任何Java程序都可以開啟JMX,然后使用JConsole或Visual VM進(jìn)行預(yù)覽。下圖是使用Jconsle通過JMX查看Java程序的運(yùn)行信息

為Java程序開啟JMX很簡單,只要在運(yùn)行Java程序的命令后面指定如下命令即可
-Djava.rmi.server.hostname=127.0.0.1
-Dcom.sun.management.jmxremote.port=1000
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
我們從Jconsole的視圖標(biāo)簽中見到,JConsole通過JMX展示的信息都是Java程序的通用信息,如內(nèi)存情況、線程情況、類加載情況等,換言之,只要是Java程序就都具備這些信息。這些信息為我們優(yōu)化程序性能、排查BUG非常有用,而JMX就是獲取這些信息的基礎(chǔ),因此它是一種非常有用的技術(shù)。
然而JMX的強(qiáng)大遠(yuǎn)不止此,它出了能提供一些通用的信息以外,還能通過特定的編程接口提供一些針對具體程序的專有信息并在JConsole等JMX客戶端工具中展示,具體點說就是程序員可以把需要展示的信息放在一種叫做MBean的Java對象內(nèi),然后JConsole之類的客戶端工具可以連接到JMX服務(wù),識別MBean并在圖形界面中顯示。從純抽象的角度觸發(fā),這其實有點像瀏覽器發(fā)送一個請求給http服務(wù)器,然后http服務(wù)器執(zhí)行瀏覽器的請求并返回相應(yīng)的數(shù)據(jù),從某種角度來說JConsole和JMX也是以這種方式工作的,只是它們使用的協(xié)議不是http,交換數(shù)據(jù)協(xié)議格式不是http數(shù)據(jù)包,但是他們的確是以客戶端/服務(wù)器這種模式工作的,而且完成的事情也差不多。
那么既然有了http,JMX又有何存在意義呢。 事實上,JMX能完成的任務(wù)通過http的確都能完成,只不過某些情況下用JMX來做會更加方便。
比如說你需要知道服務(wù)器上個運(yùn)行中程序的相關(guān)信息, 如執(zhí)行了多少次數(shù)據(jù)庫操作、任務(wù)隊列中有多少個任務(wù)在等待處理
最常用的解決方案,我們會在程序中啟動一個http服務(wù),當(dāng)接收到來自客戶端的請求這些信息的請求時,我們的http處理程序會獲得這些信息,并轉(zhuǎn)換成特定格式的數(shù)據(jù)如JSON返回給客戶端,客戶端會以某種方式展現(xiàn)這些信息。
如以JMX作為解決方案,核心流程也是如此,但在數(shù)據(jù)的交換方式上會略有不同。
下面我們展示JMX是如何完成此任務(wù)的。
一、定義一個展示所需信息的MBean接口
public interface ServerInfoMBean {
int getExecutedSqlCmdCount();
}
在使用 Standard Mbean 作為數(shù)據(jù)傳輸對象的情況下這個接口的定義是必須的, 并且接口名稱必須以“MBean”這個單詞結(jié)尾。
二、實現(xiàn)具體的MBean
public class ServerInfo implements ServerInfoMBean {
public int getExecutedSqlCmdCount() {
return Dbutil.getExecutedSqlCmdCount();
}
}
三、在程序的某個地方啟動JMX服務(wù)并注冊ServerInfoMBean
public static void main(String[] args) throws JMException, Exception{
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("serverInfoMBean:name=serverInfo");
server.registerMBean(new ServerInfo(), name);
}
四、運(yùn)行程序,并通過JConsole查看
如果程序運(yùn)行在本地,Jconsole會自動檢測到程序的進(jìn)程,鼠標(biāo)雙擊進(jìn)入即可

在JConsole下面即會展示我們定義的MBean中的內(nèi)容

那么假如Java程序并非運(yùn)行在本地而是運(yùn)行在遠(yuǎn)端服務(wù)器上我們應(yīng)該如何通過客戶端去連接呢, 很簡單,只要使用JDK提供的JMX類庫監(jiān)聽端口提供服務(wù)即可
public class Main {
public static void main(String[] args) throws JMException, Exception{
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("serverInfoMBean:name=serverInfo");
server.registerMBean(new ServerInfo(), name);
LocateRegistry.createRegistry(8081);
JMXServiceURL url = new JMXServiceURL
("service:jmx:rmi:///jndi/rmi://localhost:8081/jmxrmi");
JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
jcs.start();
}
}
或者在啟動Java程序指定命令行參數(shù)也
-Djava.rmi.server.hostname=127.0.0.1
-Dcom.sun.management.jmxremote.port=10086
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
<br />然后使用JConsole的連接遠(yuǎn)端進(jìn)程功能即可

其余的操作和本地?zé)o差。
這相對于提供一個http服務(wù)來完成任務(wù)是不是要簡單了不少,http是一個更加抽象、應(yīng)用面更廣泛、功能更強(qiáng)大的服務(wù),因此所作的工作也要更多一些。JMX則是一個更加具體、應(yīng)用面不那么廣、功能也沒有http強(qiáng)大的服務(wù),不過呢它勝在解決特定問題更加輕松方便,上面的示例已經(jīng)很好的說明了。
此外,JMX和Jconsole并不僅僅只能展示數(shù)據(jù),它還能執(zhí)行Java方法。以上面的示例為基礎(chǔ)我們再進(jìn)行一系列改進(jìn)。
一、擴(kuò)展ServerInfoMBean接口和實現(xiàn)的類
public interface ServerInfoMBean {
int getExecutedSqlCmdCount();
void printString(String fromJConsole);
}
public class ServerInfo implements ServerInfoMBean {
public int getExecutedSqlCmdCount() {
return 100;
}
public void printString(String fromJConsole) {
System.out.println(fromJConsole);
}
}
二、運(yùn)行程序并使用JConsole連接

mbean頁簽中出現(xiàn)了我們新添加的方法
三、點擊printString按鈕調(diào)用方法

方法被調(diào)用,同時控制臺也打印了通過Jconsole傳遞的參數(shù)

這里只是講解了JMX的用處和最基礎(chǔ)的使用方法,顯然JMX真正提供的功能遠(yuǎn)不及此,比如它可以不用JConsole而是客戶端編程的方式訪問等等, 有興趣的同學(xué)可以深入研究。
總而言之, 我覺得JMX是一種小巧精悍的工具,在不需要大張旗鼓的通過http或者其他server\client方式提供服務(wù)時,就是他發(fā)揮用處的時機(jī)了。