1 基本信息
摘要:本篇爲JMX的學習筆記, 教你一步一步使用JMX,並提供一個能運行的完整的案例,可以使人達到快速入門的x目的。
作者:陳光耀
2 正文
JMX(Java Management Extensions, Java管理擴展)是一個爲應用程序植入管理功能的框架。JMX是一套標準的代理和服務,實際上,用戶可以在任何Java應用程序中使用這些代理和服務實現管理。 JMX的優點在於:
1.可以非常容易的使應用程序具有被管理的功能
2.提供具有高度伸縮性的架構,每個JMX Agent服務可以很容易的放入到Agent中,每個JMX的實現都提供幾個核心的Agent服務,你也可以自己編寫服務,服務可以很容易的部署,取消部署。
3.主要提供接口,允許有不同的實現。
Jboss的成功就在於採用了JMX,從零開始、模塊化開發了Jboss服務器和容器,實現了模塊化、 嵌入式的技術架構。JMX作爲集成中心(總線),可以很方便的熱插拔新的模塊和組件。JMX服務可以通過HTTP、RMI、SNMP等多種協議進行訪問, 使其適合作爲一個網絡管理、監控平臺的技術架構。
JDK5已經內置了JMX的API,並且JVM中包含了一個平臺級MBean Server和供管理應用使用的平臺級MXBeans,並提供了一個圖形化jmx管理和監控工具來管理JMX組件。
JMX的技術架構如下:
一. 基本概念
JMX涉及到以下基本概念。
MBean:暴露用來操作和訪問被管資源的管理接口的java對象
MBean Server:管理一組MBean的java類,類似一個查找Mbean的註冊表,暴露出所有註冊過的Mbean的管理接口,以及提供查找Mbean和通知監聽器的方法。
MBean Agent:提供管理MBean的服務的一個Java進程,是MBean Server的容器。提供這些服務:建立MBean關係、動態加載類、簡單監控服務、定時器等。
MBean Agent可以指望有一組協議適配器(Adaptor)或連接器(Connector),使遠程客戶和不同客戶使用agent。協議適配器和連接器通常也是MBean。
協議適配器(Adaptor)或連接器(Connector):在MBean Agent之中的一些對象,用來將Agent暴露給管理應用和協議。
Adaptor和Connector區別: Adaptor通常要監聽進來的信息,這些信息是在某個協議如HTTP或SNMP中構造的。在這個意義上,協議適配器在任何時間都存在於Agent中並只 有一個組件。Connector由兩個組件組成,一個在Agent端,一個在客戶端。客戶使用客戶端連接器組件和服務器端連接器組件聯繫並和Agent通 訊。
下圖爲RMI Connector的兩個組件通訊情況:
管理應用:連接任意數量MBean Agent的用戶應用。
JMX Agent可以通過定製開發適配器或連接器,和一個非jmx管理應用進行交互。
通知(Notification):由MBean或MBean Server發出的,封裝了事件、警告和通用信息的Java對象。MBean或Java對象可以註冊爲監聽者來接收通知。JMX的通知模型類似於Java的事件模型。
設備(Instrumentation):使用MBean或一組MBean暴露管理資源的進程。
管理應用(Manager Appliction):使用MBean的管理應用程序。
二、JMX的三層架構
分佈層(Distributed Layer):包含了能使管理應用和JMX Agent通訊的組件
代理層(Agent Layer):包含了Agents和MBean Server
裝備層(Instrument Layer):包含了可以代表可管理資源的MBean
1、分佈層:jmx的最外層,負責向外界提供jmx的agent。
有兩種類型的分佈式交互(interaction),即建立一個連接(Connectin):
1) 通過Adaptor獲取的交互方式:通過不同的協議如HTTP,SNMP提供到MBean的可見性(visibility)。
2) JMX Agent有Connector組件,將agent的API暴露給其他分佈式技術,如RMI。
當遠程客戶端通過Adaptor或Connector和Agent建立連接後,就可以和agent中註冊的MBean進行交互。接着就進入了代理層。
2、代理層:
代理層的主要組件是MBean Server,作爲MBean的登記處,是代理層的核心。
代理層提供4個代理服務來更方便地管理MBean:定時器、監控、動態MBean 加載、關係服務。
代理層提供從管理應用到被管理資源的訪問。
Jmx代理可以運行在管理資源的機器上的JVM中,也可以位於在遠程。agent不需要知道它暴露出的資源的信息,或使用MBean的管理應用。
agent擔當了一個服務的角色,用來處理MBean,允許通過暴露出的Connector或Adaptor的一系列協議來操作MBean。
3、裝備層:離被管理資源最近的一層。由註冊在Agent上的MBean組成。
每個MBean暴露一個底層資源的一塊配置和功能,並通過一個Java對象來提供。如果底層資源不使用Java, 則MBean充當一個翻譯器。
MBean是一個輕量級的類,知道如何使用、獲取操作其資源,並向agent和用戶提供訪問途徑和功能。
使用JMX作爲應用程序架構:
jmx 代理層用來構件應用很理想。MBean Server可以用作應用組件,如數據層、日誌組件、事務管理器的骨架。
使用這種架構,開發人員可以很容易地從服務器中增加、改變、刪除應用服務。
三、 Getting Started:簡單MBean
運行本文程序需要在JDK5.x環境下,並且要到 java.sun.com網站上去下載Sun的JDMK5.1(Java Dynamic Management Kit 5.1),下載後解壓,將lib目錄下的jdmktk.jar文件加到項目的類路徑上即可(如果找不到,可以向我索取 [email protected] )。
1. 定義接口4. 運行
在IE中打入http://localhost:9092/
- package jmxbook.ch2;
- public interface HelloWorldMBean {
- public void setGreeting(String greeting);
- public String getGreeting();
- public void printGreeting();
- }
2. 編寫Mbean實現類
- package jmxbook.ch2;
- public class HelloWorld implements HelloWorldMBean {
- private String greeting=null;
- public HelloWorld() {
- this.greeting="I'm a standard MBean";
- }
- public HelloWorld(String greeting) {
- this.greeting=greeting;
- }
- public String getGreeting() {
- return greeting;
- }
- public void printGreeting() {
- System.out.println(greeting);
- }
- public void setGreeting(String greeting) {
- this.greeting=greeting;
- }
- }
3. 編寫Agent
- package jmxbook.ch2;
- import javax.management.MBeanServer;
- import javax.management.MBeanServerFactory;
- import javax.management.ObjectName;
- import com.sun.jdmk.comm.HtmlAdaptorServer;
- public class HelloAgent {
- private MBeanServer server = null;
- public HelloAgent() {
- server = MBeanServerFactory.createMBeanServer("HelloAgent");
- HtmlAdaptorServer adapter = new HtmlAdaptorServer();
- HelloWorld hw = new HelloWorld();
- ObjectName adapterName = null;
- ObjectName mbeanName = null;
- try {
- mbeanName = new ObjectName("HelloAgent:name=helloWrold");
- server.registerMBean(hw, mbeanName);
- adapterName = new ObjectName(
- "HelloAgent:name=htmlAdaptor,port=9092");
- adapter.setPort(9092);
- server.registerMBean(adapter, adapterName);
- adapter.start();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public static void main(String args[]) {
- System.out.println("HelloAgent is running");
- HelloAgent agent = new HelloAgent();
- }
- }
說明:
1) 區分Mbean: 使用ObjcetName對象,分兩部分:1) 域名 2) key-value對列表
如 HelloAgent:name=helloWorld
兩個ObjectName的相等:
ObjectName objName1=new ObjectName(“HelloAgent:name=helloWorld,type=typeA”);
ObjectName objName2=new ObjectName(“HelloAgent: type=typeA,name=helloWorld”);
則objName1.equals(objName2)返回true.
2) ObjectName衝突: MBean Server註冊兩個相同ObjectName的MBean會拋出異常.
5.使用html客戶端訪問jmx agent:
1) Agent View: 顯示所有註冊的MBean
2) MBean View: 顯示一個MBean信息
3) Admin View: 管理MBean,註冊和取消註冊MBean
輸入key和java className, action選擇Constrctors, 可以查看類的構造函數列表(需要MBean的接口命名爲 名稱+MBean, 如HelloWorldMBean), 輸入構造函數的參數(有的話),按create可以建立新的MBean並註冊到agent.
四.使用通知:
jmx可以使用通知機制,從一個MBean發送通知給另一個MBean,如下圖:
使用通知的步驟如下:
1) 建立通知發送者:
兩種方式:
(1) 實現javax.management.NotificationBroadcaster接口
(2) 擴展 javax.management.NotificationBroadcasterSupport類
改造HelloWorld.java
- package jmxbook.ch2.notification;
- import javax.management.Notification;
- import javax.management.NotificationBroadcasterSupport;
- public class HelloWorld extends NotificationBroadcasterSupport
- implements HelloWorldMBean {
- public HelloWorld() {
- this.greeting = "I'm a Notification Sender";
- }
- public HelloWorld(String greeting) {
- this.greeting = greeting;
- }
- public void setGreeting(String greeting) {
- this.greeting = greeting;
- Notification notification = new Notification(
- "jmxbook.ch2.helloWorld.test", this, -1, System
- .currentTimeMillis(), greeting);
- sendNotification(notification);
- }
- public String getGreeting() {
- return greeting;
- }
- public void printGreeting() {
- System.out.println(greeting);
- }
- private String greeting;
- }
2) 建立通知接收者:
接口: MyListenerMBean.java:
- package jmxbook.ch2.notification;
- import javax.management.NotificationListener;
- public interface MyListenerMBean extends NotificationListener {
- public void printInfo(String message);
- }
實現類: MyListener.java
- package jmxbook.ch2.notification;
- import javax.management.Notification;
- public class MyListener implements MyListenerMBean {
- public void printInfo(String message) {
- System.out.println(message);
- }
- public void handleNotification(Notification notification, Object handback) {
- this.printInfo("My listener recieve Nofitication: " + notification.getType() + " "
- + notification.getMessage());
- }
- }
3) 改造HelloAgent:
- package jmxbook.ch2.notification;
- import javax.management.*;
- import com.sun.jdmk.comm.HtmlAdaptorServer;
- public class HelloAgent implements NotificationListener {
- private MBeanServer mbs = null;
- public HelloAgent() {
- mbs = MBeanServerFactory.createMBeanServer("HelloAgent");
- HtmlAdaptorServer adapter = new HtmlAdaptorServer();
- HelloWorld hw = new HelloWorld();
- ObjectName adapterName = null;
- ObjectName helloWorldName = null;
- try {
- adapterName = new ObjectName(
- "HelloAgent:name=htmladapter,port=9092");
- mbs.registerMBean(adapter, adapterName);
- adapter.setPort(9092);
- adapter.start();
- MyListener listener = new MyListener();
- mbs.registerMBean(listener, new ObjectName(
- "HelloAgent:name=myListener"));
- helloWorldName = new ObjectName(
- "HelloAgent:name=helloWorld,notification=yes");
- mbs.registerMBean(hw, helloWorldName);
- hw.addNotificationListener(this, null, null);
- hw.addNotificationListener(listener, null, null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }// constructor
- public void handleNotification(Notification notif, Object handback) {
- System.out.println("My listener recieve Nofitication: "
- + notif.getType() + " " + notif.getMessage());
- }
- public static void main(String args[]) {
- HelloAgent agent = new HelloAgent();
- System.out.println("HelloAgent is running");
- }
- }
4) 運行:
調用HelloWorld的greeting(“Can I help you?”)方法, 控制檯顯示:
Agent recieve Nofitication: jmxbook.ch2.helloWorld.test Can I help you?
My listener recieve Nofitication: jmxbook.ch2.helloWorld.test Can I help you?
五.使用RMI Connector:
1) 改造Agent:
- package jmxbook.ch3;
- import com.sun.jdmk.comm.*;
- import javax.management.*;
- public class JMXBookAgent {
- private MBeanServer server = null;
- public JMXBookAgent() {
- System.out.println("\n\tCREATE the MBeanServer.");
- server = MBeanServerFactory.createMBeanServer("JMXBookAgent");
- startHTMLAdapter();
- startRMIConnector();
- }
- protected void startHTMLAdapter() {
- HtmlAdaptorServer adapter = new HtmlAdaptorServer();
- ObjectName adapterName = null;
- try {
- adapter.setPort(9092);
- adapterName = new ObjectName("JMXBookAgent:name=html,port=9092");
- server.registerMBean(adapter, adapterName);
- adapter.start();
- } catch (Exception e) {
- ExceptionUtil.printException(e);
- System.out.println("Error Starting HTML Adapter for Agent");
- }
- }
- protected void startRMIConnector() {
- RmiConnectorServer connector = new RmiConnectorServer();
- ObjectName connectorName = null;
- try {
- connector.setPort(2099);
- connectorName = new ObjectName("JMXBookAgent:name=RMIConnector");
- server.registerMBean(connector, connectorName);
- connector.start();
- } catch (Exception e) {
- ExceptionUtil.printException(e);
- }
- }
- public static void main(String[] args) {
- System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~"
- + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
- System.out.println("\n>>> START of JMXBook Agent");
- System.out.println("\n>>> CREATE the agent...");
- JMXBookAgent agent = new JMXBookAgent();
- System.out.println("\nAgent is Ready for Service...\n");
- }
- }
2) 添加異常顯示類
- package jmxbook.ch3;
- import javax.management.*;
- public class ExceptionUtil {
- public static void printException(Exception e) {
- System.out.println("-------[ Exception ]-------");
- e.printStackTrace();
- if (e instanceof MBeanException) {
- boolean hasEmbeddedExceptions = true;
- Exception embeddedExc = e;
- while (hasEmbeddedExceptions) {
- embeddedExc = ((MBeanException) embeddedExc)
- .getTargetException();
- System.out.println("-------[ Embedded Exception ]-------");
- embeddedExc.printStackTrace();
- if (!(embeddedExc instanceof MBeanException)) {
- hasEmbeddedExceptions = false;
- }
- }
- }
- }
- }
3) RMI 工廠
- package jmxbook.ch3;
- import com.sun.jdmk.comm.RmiConnectorAddress;
- import com.sun.jdmk.comm.RmiConnectorClient;
- public class RMIClientFactory {
- public static RmiConnectorClient getClient() {
- RmiConnectorClient client = new RmiConnectorClient();
- RmiConnectorAddress address = new RmiConnectorAddress();
- address.setPort(2099);
- System.out.println("\t\tTYPE\t= " + address.getConnectorType());
- System.out.println("\t\tPORT\t= " + address.getPort());
- System.out.println("\t\tHOST\t= " + address.getHost());
- System.out.println("\t\tSERVER\t= " + address.getName());
- try {
- client.connect(address);
- } catch (Exception e) {
- ExceptionUtil.printException(e);
- }
- return client;
- }
- }
5) 建立RMI客戶端:
- package jmxbook.ch3;
- import javax.management.*;
- import jmxbook.ch2.*;
- import com.sun.jdmk.comm.*;
- public class MBeanSetup {
- public MBeanSetup() {
- try {
- RmiConnectorClient client = RMIClientFactory.getClient();
- ObjectName hwName = new ObjectName("JMXBookAgent:name=helloWorld");
- client.createMBean("jmxbook.ch2.HelloWorld", hwName);
- client.invoke(hwName, "printGreeting", null, null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public static void main(String args[]) {
- MBeanSetup setup = new MBeanSetup();
- }
- }
6) 運行:
運行agent:
- java -cp ..\lib\jdmkrt.jar;. jmxbook.ch3.JMXBookAgent
- >>> START of JMXBook Agent
- >>> CREATE the agent...
- CREATE the MBeanServer.
- Agent is Ready for Service...
運行客戶端:
- java -cp ..\lib\jdmkrt.jar;. jmxbook.ch3.MBeanSetup
- TYPE = SUN RMI
- PORT = 2099
- HOST = chengy
- SERVER = name=RmiConnectorServer