原文位址: http://docs.oracle.com/javase/tutorial/jmx/remote/index.html
Lesson: Remote Management
JMX API能夠確定你使用基于JMX技術的連接配接器(JMX connectors)去執行對資源的遠端管理。JMX連接配接器使基于Java的遠端用戶端連接配接伺服器成為了可能。一個作為連接配接器的用戶端基本解耦和MBean server的相同。
一個JMX連接配接器包括一個連接配接用戶端和一個連接配接伺服器。連接配接伺服器是MBean伺服器用于監聽來自于用戶端請求。連接配接器用戶端用于和連接配接器伺服器履歷連接配接。連接配接器用戶端和連接配接器伺服器通常不運作在同一個Java虛拟機上。JMX API定義了基于RMI的标準連接配接協定。這個協定用于連接配接一個JMX用戶端到MBean伺服器,然後操作注冊的MBean像在本地操作一樣。
Java平台提供了一個使用JMX API的标準RMI連接配接器可以開箱即用的監控應用。開箱即用的RMI連接配接器自動的暴露應用給遠端的管理不需要你顯式的定義遠端連接配接服務。開箱即用的管理代理在你啟動應用的時候自動激活。基于JMX技術的監控和管理程式可以在遠端監控這些應用。
Exposing a Resource for Remote Management By JConsole
如果你使用開箱即用的遠端管理監控工具例如: JConsole, 暴露你的使用JMX API的應用給遠端的管理非常簡單。
如果要暴露你的應用給遠端的管理你需要用合适的方法啟動。這個例子展示了如何暴露你的Main JMX代理給遠端管理。
安全建議:
為了簡單的目的,這個例子沒有開啟認證和加密。然而,你應該在實際的生産環境中你用該實作這些安全機制。接下來的文檔提供了如何去激活安全機制。
這個例子需要JDK1.6或以後的版本。為了遠端監控Main JMX代理,你需要下面的步驟:
1.如果你還沒有做好,儲存jmx_examples.zip到你的工作目錄。
2.在終端視窗中解壓成簡單的類:
unzip jmx_examples.zip
3.在工作目錄中編譯這些類:
javac com/example/*.java
4.運作Main應用,指定屬性去暴露應用給遠端的管理。在windows裡面使用(^)代替換行符(\) 來完成一個長指令的換行。
java -Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
com.example.Main
Main的等待事情發生的确認資訊已經生成。
5.在不同的機器的不同終端視窗口打開JConsole
jconsole
6.選擇遠端程序,并且輸入遠端程序域:
hostname:9999
在這個位址裡,hostName是Main程式運作的遠端機器的IP,9999是進行遠端管理的端口号。
7.點選連接配接
關于Main的JVM概況顯示出來了
8.點選MBeans标簽
這個面闆展示了目前MBean server上面注冊的所有MBean
9. 在窗體的左部,展開MBean樹的com.example節點
你可以看到Hello已經被Main注冊。如果你點選Hello,你可以看到MBean樹節點的屬性和操作,盡管他們運作在不同的機器上。
10.關閉并且退出JConsole
Creating a Custom JMX Client
之前的章節介紹了如何建立一個JMX技術的MBeans或MXBeans,然後注冊他們到JMX代理。然而,所有的例子都使用存在的JMX用戶端,這節介紹了如何建立一個自定義的JMX用戶端。
一個通用用戶端的例子,client被包括在jmx_examples.zip。這個JMX用戶端和之前章節裡面相同的MBean,MXBean和JMX代理互動。
導入遠端JMX API類
為了讓你的JMX用戶端具備連接配接遠端JMX代理的能力,你需要使用javax.management.remote包下的這些類。
package com.example;
...
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class Client {
...
這個用戶端建立了一個JMXConnector執行個體,他需要一個JMXConnectorFactory和JMXServiceURL。
建立一個通知監聽器
JMX用戶端需要監聽并且處理一些在JMX代理的MBean server中注冊的MBeans發送的消息。JMX用戶端的通知處理是NotificationListener接口的執行個體,如下:
...
public static class ClientListener implements NotificationListener {
public void handleNotification(Notification notification,
Object handback) {
echo("\nReceived notification:");
echo("\tClassName: " + notification.getClass().getName());
echo("\tSource: " + notification.getSource());
echo("\tType: " + notification.getType());
echo("\tMessage: " + notification.getMessage());
if (notification instanceof AttributeChangeNotification) {
AttributeChangeNotification acn =
(AttributeChangeNotification) notification;
echo("\tAttributeName: " + acn.getAttributeName());
echo("\tAttributeType: " + acn.getAttributeType());
echo("\tNewValue: " + acn.getNewValue());
echo("\tOldValue: " + acn.getOldValue());
}
}
}
...
這個通知監聽器接收到任何通知,然後檢索存儲在通知裡的資訊。然後根據接收到的通知類型執行不同的操作。在這裡,監聽者即受到AttributeChangeNotification類型的通知,通過調用AttributeChangeNotification的getAttributeName, getAttributeType, getNewValue 和 getOldValue方法可以擷取改變屬性的名字,類型和新、老值.
可以通過下面的代碼建立一個新的ClientListener執行個體。
ClientListener listener = new ClientListener();
建立一個RMI連接配接器用戶端
這個用戶端建立了一個RMI連接配接去連接配接Main代理建立的RMI連接配接伺服器。它允許JMX用戶端和JMX代理運作在同一台機器上。
...
public static void main(String[] args) throws Exception {
echo("\nCreate an RMI connector client and " +
"connect it to the RMI connector server");
JMXServiceURL url =
new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
...
正如你看到的,client定義了一個名字叫做url的JMXServiceURL執行個體,它描述了用戶端想要連接配接的伺服器的位置。這個URL允許用戶端檢索注冊在9999端口的本地和遠端位址jmx rmi服務。
伴随着唯一的RMI系統資料庫,連接配接器用戶端也被建立。jmxc連接配接用戶端是JMXConnector的一個執行個體,調用JMXConnectorFactory的connect()方法建立。調用connect()方法的時候傳遞了一個url參數和null的環境map。
連接配接到遠端的MBean伺服器
RMI連接配接被建立的時候,JMX用戶端必須連接配接到遠端的MBean server,以便他可以和遠端JMX代理上注冊的各種MBeans互動。
...
MBeanServerConnection mbsc =
jmxc.getMBeanServerConnection();
...
一個叫做mbsc的MBeanServerConnection被建立,通過調用JMXConnector執行個體jmxc的getMBeanServerConnection()方法。
現在連接配接用戶端已經連接配接到了MBean server建立的JMX代理,并且操作注冊的MBeans對兩頭都是透明的。
首先,用戶端定義了一些簡單的操作來獲得MBean代理上的MBeans資訊。
...
echo("\nDomains:");
String domains[] = mbsc.getDomains();
Arrays.sort(domains);
for (String domain : domains) {
echo("\tDomain = " + domain);
}
...
echo("\nMBeanServer default domain = " + mbsc.getDefaultDomain());
echo("\nMBean count = " + mbsc.getMBeanCount());
echo("\nQuery MBeanServer MBeans:");
Set<ObjectName> names =
new TreeSet<ObjectName>(mbsc.queryNames(null, null));
for (ObjectName name : names) {
echo("\tObjectName = " + name);
}
...
用戶端調用了MBeanServerConnection的許多方法去擷取MBean server上面注冊的MBeans數量、要操作的域以及每個MBeans的對象名字。
通過MBeans代理在遠端MBeans上執行操作
用戶端通過調用Hello MBean的代理去和遠端的MBean server互動。這個MBean代理位于本地,模仿了遠端的MBean。
...
ObjectName mbeanName = new ObjectName("com.example:type=Hello");
HelloMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName,
HelloMBean.class, true);
echo("\nAdd notification listener...");
mbsc.addNotificationListener(mbeanName, listener, null, null);
echo("\nCacheSize = " + mbeanProxy.getCacheSize());
mbeanProxy.setCacheSize(150);
echo("\nWaiting for notification...");
sleep(2000);
echo("\nCacheSize = " + mbeanProxy.getCacheSize());
echo("\nInvoke sayHello() in Hello MBean...");
mbeanProxy.sayHello();
echo("\nInvoke add(2, 3) in Hello MBean...");
echo("\nadd(2, 3) = " + mbeanProxy.add(2, 3));
waitForEnterPressed();
...
MBean代理允許你通過Java接口通路MBean而不是寫重量級的通路遠端MBean代碼。Hello的MBean代理被通過調用javax.management.JMX類的newMBeanProxy()方法建立,傳遞了MBean的MBeanServerConnection,對戲那個名字和true,意味着代理是一個NotificationBroadcaster。JMX用戶端現在可以執行Hello定義的操作就像本地注冊的MBean一樣。JMX用戶端也可以增加一個通知監聽器然後改變MBean的CacheSize屬性,然後發送一個通知。
你也可以想建立MBeans代理一樣建立MXBeans代理。
...
ObjectName mxbeanName = new ObjectName ("com.example:type=QueueSampler");
QueueSamplerMXBean mxbeanProxy = JMX.newMXBeanProxy(mbsc,
mxbeanName, QueueSamplerMXBean.class);
QueueSample queue1 = mxbeanProxy.getQueueSample();
echo("\nQueueSample.Date = " + queue1.getDate());
echo("QueueSample.Head = " + queue1.getHead());
echo("QueueSample.Size = " + queue1.getSize());
echo("\nInvoke clearQueue() in QueueSampler MXBean...");
mxbeanProxy.clearQueue();
QueueSample queue2 = mxbeanProxy.getQueueSample();
echo("\nQueueSample.Date = " + queue2.getDate());
echo("QueueSample.Head = " + queue2.getHead());
echo("QueueSample.Size = " + queue2.getSize());
...
正如上面看到的你想要建立MXBean代理,你需要調用JMX.newMXBeanProxy來替代newMBeanProxy方法。MXBean代理mxbeanProxy 允許用戶端調用QueueSample 的操作像在本地一樣。
關閉連接配接
一旦JMX用戶端擷取了所有的需要在遠端JMX代理上面注冊的MBeans的資訊,這個連接配接诶必須被關閉。
jmxc.close();
關閉連接配接需要調用JMXConnector.close方法。
運作自定義的JMX用戶端例子
這個例子運作在JDK1.6或以後的版本,為了使用JMX用戶端client檢測遠端的JMX代理Main,你需要做以下幾步:
1.如果你還沒有做好,儲存jmx_examples.zip到你的工作目錄。
2.在終端視窗中輸入指令解壓檔案
unzip jmx_examples.zip
3.在工作目錄中編譯這些類
javac com/example/*.java
4.啟動Main應用
java -Dcom.sun.management.jmxremote.port=9999 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
com.example.Main
5.在不同的終端視窗中開啟Client
java com.example.Client
MBeanServerConnection擷取的資訊被展示
6.按Enter(回車)
Main的MBean server上注冊的所有MBeans被展示出來
7.再按一次回車
MBean serve上注冊的MBeans數目展示出來了。這些MBeans的對象名字也被展示出來。包括Main注冊的Hello MBean和QueueSampler MXBean。
8.再按一次回車
Client調用了Hello MBean的操作:
一個通知監聽器被添加到Client用于監聽Main發出的通知
CacheSize屬性從200變為150
在開啟Main的終端視窗中,CacheSize屬性改變的資訊被展示。
在開啟Client的終端視窗中,Main的通知 CacheZize屬性改變的通知被展示
HelloMBean的sayHello操作被調用
在開啟Main的終端視窗中,“Hello world”資訊被展示
使用參數2和3調用Hello MBean的add操作,然後這個結果展示在Client
9.再按一次回車
Client調用了QueueSampler MBean的操作:
QueueSampler的date,head和size被展示。
clearQueue操作被調用
10.再按一次回車
Cilent用戶端被關閉。