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入门篇_stepByStep_休闲

一. 基本概念

  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的两个组件通讯情况:
JMX入门篇_stepByStep_休闲_02

管理应用:连接任意数量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
JMX入门篇_stepByStep_休闲_03

1、分布层:jmx的最外层,负责向外界提供jmx的agent。

  有两种类型的分布式交互(interaction),即建立一个连接(Connectin):
1) 通过Adaptor获取的交互方式:通过不同的协议如HTTP,SNMP提供到MBean的可见性(visibility)。
2) JMX Agent有Connector组件,将agent的API暴露给其他分布式技术,如RMI。
JMX入门篇_stepByStep_职场_04

当远程客户端通过Adaptor或Connector和Agent建立连接后,就可以和agent中注册的MBean进行交互。接着就进入了代理层。

2、代理层:

代理层的主要组件是MBean Server,作为MBean的登记处,是代理层的核心。
代理层提供4个代理服务来更方便地管理MBean:定时器、监控、动态MBean 加载、关系服务。
代理层提供从管理应用到被管理资源的访问。
Jmx代理可以运行在管理资源的机器上的JVM中,也可以位于在远程。agent不需要知道它暴露出的资源的信息,或使用MBean的管理应用。
agent担当了一个服务的角色,用来处理MBean,允许通过暴露出的Connector或Adaptor的一系列协议来操作MBean。
JMX入门篇_stepByStep_休闲_05

3、装备层:离被管理资源最近的一层。由注册在Agent上的MBean组成。

每个MBean暴露一个底层资源的一块配置和功能,并通过一个Java对象来提供。如果底层资源不使用Java, 则MBean充当一个翻译器。
MBean是一个轻量级的类,知道如何使用、获取操作其资源,并向agent和用户提供访问途径和功能。
JMX入门篇_stepByStep_休闲_06

使用JMX作为应用程序架构:
JMX入门篇_stepByStep_jmx_07

  jmx 代理层用来构件应用很理想。MBean Server可以用作应用组件,如数据层、日志组件、事务管理器的骨架。

  使用这种架构,开发人员可以很容易地从服务器中增加、改变、删除应用服务。

三、 Getting Started:简单MBean

  运行本文程序需要在JDK5.x环境下,并且要到 java.sun.com网站上去下载Sun的JDMK5.1(Java Dynamic Management Kit 5.1),下载后解压,将lib目录下的jdmktk.jar文件加到项目的类路径上即可(如果找不到,可以向我索取 chengy@primeton.com )。

  1. 定义接口4. 运行
在IE中打入http://localhost:9092/

  1. package jmxbook.ch2;
  2.  
  3. public interface HelloWorldMBean {
  4. public void setGreeting(String greeting);
  5.  
  6. public String getGreeting();
  7.  
  8. public void printGreeting();
  9. }

2. 编写Mbean实现类

  1. package jmxbook.ch2;
  2.  
  3. public class HelloWorld implements HelloWorldMBean {
  4. private String greeting=null;
  5.  
  6. public HelloWorld() {
  7. this.greeting="I'm a standard MBean";
  8. }
  9.  
  10. public HelloWorld(String greeting) {
  11. this.greeting=greeting;
  12. }
  13.  
  14. public String getGreeting() {
  15. return greeting;
  16. }
  17.  
  18. public void printGreeting() {
  19. System.out.println(greeting);
  20. }
  21.  
  22. public void setGreeting(String greeting) {
  23. this.greeting=greeting;
  24. }
  25. }

3. 编写Agent

  1. package jmxbook.ch2;
  2.  
  3. import javax.management.MBeanServer;
  4. import javax.management.MBeanServerFactory;
  5. import javax.management.ObjectName;
  6. import com.sun.jdmk.comm.HtmlAdaptorServer;
  7.  
  8. public class HelloAgent {
  9. private MBeanServer server = null;
  10.  
  11. public HelloAgent() {
  12. server = MBeanServerFactory.createMBeanServer("HelloAgent");
  13.  
  14. HtmlAdaptorServer adapter = new HtmlAdaptorServer();
  15.  
  16. HelloWorld hw = new HelloWorld();
  17.  
  18. ObjectName adapterName = null;
  19. ObjectName mbeanName = null;
  20.  
  21. try {
  22. mbeanName = new ObjectName("HelloAgent:name=helloWrold");
  23. server.registerMBean(hw, mbeanName);
  24.  
  25. adapterName = new ObjectName(
  26. "HelloAgent:name=htmlAdaptor,port=9092");
  27. adapter.setPort(9092);
  28. server.registerMBean(adapter, adapterName);
  29. adapter.start();
  30.  
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. }
  34.  
  35. }
  36.  
  37. public static void main(String args[]) {
  38. System.out.println("HelloAgent is running");
  39. HelloAgent agent = new HelloAgent();
  40. }
  41. }

说明:
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
JMX入门篇_stepByStep_职场_08
2) MBean View: 显示一个MBean信息
JMX入门篇_stepByStep_jmx_09
3) Admin View: 管理MBean,注册和取消注册MBean
JMX入门篇_stepByStep_jmx_10

  输入key和java className, action选择Constrctors, 可以查看类的构造函数列表(需要MBean的接口命名为 名称+MBean, 如HelloWorldMBean), 输入构造函数的参数(有的话),按create可以建立新的MBean并注册到agent.
JMX入门篇_stepByStep_jmx_11

四.使用通知:

  jmx可以使用通知机制,从一个MBean发送通知给另一个MBean,如下图:
JMX入门篇_stepByStep_jmx_12

使用通知的步骤如下:
1) 建立通知发送者:
两种方式:
(1) 实现javax.management.NotificationBroadcaster接口
(2) 扩展 javax.management.NotificationBroadcasterSupport类

改造HelloWorld.java

  1. package jmxbook.ch2.notification;
  2.  
  3. import javax.management.Notification;
  4. import javax.management.NotificationBroadcasterSupport;
  5.  
  6. public class HelloWorld extends NotificationBroadcasterSupport
  7. implements HelloWorldMBean {
  8. public HelloWorld() {
  9. this.greeting = "I'm a Notification Sender";
  10. }
  11.  
  12. public HelloWorld(String greeting) {
  13. this.greeting = greeting;
  14. }
  15.  
  16. public void setGreeting(String greeting) {
  17. this.greeting = greeting;
  18. Notification notification = new Notification(
  19. "jmxbook.ch2.helloWorld.test", this, -1, System
  20. .currentTimeMillis(), greeting);
  21. sendNotification(notification);
  22. }
  23.  
  24. public String getGreeting() {
  25. return greeting;
  26. }
  27.  
  28. public void printGreeting() {
  29. System.out.println(greeting);
  30. }
  31.  
  32. private String greeting;
  33. }

JMX入门篇_stepByStep_休闲_13

2) 建立通知接收者:
接口: MyListenerMBean.java:

  1. package jmxbook.ch2.notification;
  2.  
  3. import javax.management.NotificationListener;
  4.  
  5. public interface MyListenerMBean extends NotificationListener {
  6. public void printInfo(String message);
  7. }


实现类: MyListener.java

  1. package jmxbook.ch2.notification;
  2.  
  3. import javax.management.Notification;
  4.  
  5. public class MyListener implements MyListenerMBean {
  6.  
  7. public void printInfo(String message) {
  8. System.out.println(message);
  9. }
  10.  
  11. public void handleNotification(Notification notification, Object handback) {
  12. this.printInfo("My listener recieve Nofitication: " + notification.getType() + " "
  13. + notification.getMessage());
  14. }
  15. }

3) 改造HelloAgent:

  1. package jmxbook.ch2.notification;
  2.  
  3. import javax.management.*;
  4. import com.sun.jdmk.comm.HtmlAdaptorServer;
  5.  
  6. public class HelloAgent implements NotificationListener {
  7. private MBeanServer mbs = null;
  8.  
  9. public HelloAgent() {
  10. mbs = MBeanServerFactory.createMBeanServer("HelloAgent");
  11. HtmlAdaptorServer adapter = new HtmlAdaptorServer();
  12.  
  13. HelloWorld hw = new HelloWorld();
  14. ObjectName adapterName = null;
  15. ObjectName helloWorldName = null;
  16. try {
  17. adapterName = new ObjectName(
  18. "HelloAgent:name=htmladapter,port=9092");
  19. mbs.registerMBean(adapter, adapterName);
  20. adapter.setPort(9092);
  21. adapter.start();
  22.  
  23. MyListener listener = new MyListener();
  24. mbs.registerMBean(listener, new ObjectName(
  25. "HelloAgent:name=myListener"));
  26.  
  27. helloWorldName = new ObjectName(
  28. "HelloAgent:name=helloWorld,notification=yes");
  29. mbs.registerMBean(hw, helloWorldName);
  30. hw.addNotificationListener(this, null, null);
  31. hw.addNotificationListener(listener, null, null);
  32. } catch (Exception e) {
  33. e.printStackTrace();
  34. }
  35. }// constructor
  36.  
  37. public void handleNotification(Notification notif, Object handback) {
  38. System.out.println("My listener recieve Nofitication: "
  39. + notif.getType() + " " + notif.getMessage());
  40. }
  41.  
  42. public static void main(String args[]) {
  43. HelloAgent agent = new HelloAgent();
  44. System.out.println("HelloAgent is running");
  45. }
  46. }

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:

  1. package jmxbook.ch3;
  2.  
  3. import com.sun.jdmk.comm.*;
  4. import javax.management.*;
  5.  
  6. public class JMXBookAgent {
  7. private MBeanServer server = null;
  8.  
  9. public JMXBookAgent() {
  10. System.out.println("\n\tCREATE the MBeanServer.");
  11. server = MBeanServerFactory.createMBeanServer("JMXBookAgent");
  12. startHTMLAdapter();
  13. startRMIConnector();
  14. }
  15.  
  16. protected void startHTMLAdapter() {
  17. HtmlAdaptorServer adapter = new HtmlAdaptorServer();
  18. ObjectName adapterName = null;
  19. try {
  20. adapter.setPort(9092);
  21. adapterName = new ObjectName("JMXBookAgent:name=html,port=9092");
  22. server.registerMBean(adapter, adapterName);
  23. adapter.start();
  24. } catch (Exception e) {
  25. ExceptionUtil.printException(e);
  26. System.out.println("Error Starting HTML Adapter for Agent");
  27. }
  28. }
  29.  
  30. protected void startRMIConnector() {
  31. RmiConnectorServer connector = new RmiConnectorServer();
  32. ObjectName connectorName = null;
  33. try {
  34. connector.setPort(2099);
  35. connectorName = new ObjectName("JMXBookAgent:name=RMIConnector");
  36. server.registerMBean(connector, connectorName);
  37. connector.start();
  38. } catch (Exception e) {
  39. ExceptionUtil.printException(e);
  40. }
  41. }
  42.  
  43. public static void main(String[] args) {
  44. System.out.println("\n~~~~~~~~~~~~~~~~~~~~~~~"
  45. + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  46. System.out.println("\n>>> START of JMXBook Agent");
  47. System.out.println("\n>>> CREATE the agent...");
  48. JMXBookAgent agent = new JMXBookAgent();
  49. System.out.println("\nAgent is Ready for Service...\n");
  50. }
  51. }


2) 添加异常显示类

  1. package jmxbook.ch3;
  2.  
  3. import javax.management.*;
  4.  
  5. public class ExceptionUtil {
  6. public static void printException(Exception e) {
  7. System.out.println("-------[ Exception ]-------");
  8. e.printStackTrace();
  9. if (e instanceof MBeanException) {
  10. boolean hasEmbeddedExceptions = true;
  11. Exception embeddedExc = e;
  12. while (hasEmbeddedExceptions) {
  13. embeddedExc = ((MBeanException) embeddedExc)
  14. .getTargetException();
  15. System.out.println("-------[ Embedded Exception ]-------");
  16. embeddedExc.printStackTrace();
  17. if (!(embeddedExc instanceof MBeanException)) {
  18. hasEmbeddedExceptions = false;
  19. }
  20. }
  21. }
  22. }
  23. }

3) RMI 工厂

  1. package jmxbook.ch3;
  2.  
  3. import com.sun.jdmk.comm.RmiConnectorAddress;
  4. import com.sun.jdmk.comm.RmiConnectorClient;
  5.  
  6.  
  7. public class RMIClientFactory {
  8. public static RmiConnectorClient getClient() {
  9. RmiConnectorClient client = new RmiConnectorClient();
  10. RmiConnectorAddress address = new RmiConnectorAddress();
  11. address.setPort(2099);
  12. System.out.println("\t\tTYPE\t= " + address.getConnectorType());
  13. System.out.println("\t\tPORT\t= " + address.getPort());
  14. System.out.println("\t\tHOST\t= " + address.getHost());
  15. System.out.println("\t\tSERVER\t= " + address.getName());
  16. try {
  17. client.connect(address);
  18. } catch (Exception e) {
  19. ExceptionUtil.printException(e);
  20. }
  21. return client;
  22. }
  23. }

5) 建立RMI客户端:

  1. package jmxbook.ch3;
  2.  
  3. import javax.management.*;
  4. import jmxbook.ch2.*;
  5. import com.sun.jdmk.comm.*;
  6.  
  7. public class MBeanSetup {
  8. public MBeanSetup() {
  9. try {
  10. RmiConnectorClient client = RMIClientFactory.getClient();
  11. ObjectName hwName = new ObjectName("JMXBookAgent:name=helloWorld");
  12. client.createMBean("jmxbook.ch2.HelloWorld", hwName);
  13. client.invoke(hwName, "printGreeting", null, null);
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. }
  18.  
  19. public static void main(String args[]) {
  20. MBeanSetup setup = new MBeanSetup();
  21. }
  22. }

6) 运行:
运行agent:

  1. java -cp ..\lib\jdmkrt.jar;. jmxbook.ch3.JMXBookAgent
  2.  
  3. >>> START of JMXBook Agent
  4.  
  5. >>> CREATE the agent...
  6.  
  7. CREATE the MBeanServer.
  8.  
  9. Agent is Ready for Service...

运行客户端:

  1. java -cp ..\lib\jdmkrt.jar;. jmxbook.ch3.MBeanSetup
  2.  
  3. TYPE = SUN RMI
  4. PORT = 2099
  5. HOST = chengy
  6. SERVER = name=RmiConnectorServer