JMX教程


2.0JMX技术概览:


JMX是Java Management Extensions 的简写,即Java管理扩展。从名字中就可以看出,这门技术是对Java应用程序和JVM进行监控和管理的。

在企业实际开发过程中,所有的程序都是需要进行监控的。没有监控,程序就相当于是裸奔。

QQ截图20160113230655.png

在一些小公司可能没有监控,只注重于功能,但是在大公司中,没有监控是绝对不可能的。

JMX是Java官方提供的一套用于监控Java程序和JVM运行时状态的标准API。通过JMX,我们可以监控的内容包括:

1、服务器中各种资源的使用情况:如CPU、内存等

2、JVM内存使用情况

3、JVM中的线程情况

4、JVM中加载的类

....

        相信通过这些介绍,读者已经感受到了JMX的强大功能。而且因为JMX是J2SE平台的标准组成部分,意味着我们使用JMX时,不需要引入任何第三方jar包,直接可以进行开发。远离裸奔,从JMX开始!


2.1 JMX技术架构

本节讲解JMX的技术架构, JMX技术可以被分为3层,如下:

    * 监测(Instrumentation)
    * JMX代理
    * 远程管理


如下图:

乍一看这张图,读者可能会比较晕,不过没关系,下文将会详细的解释。我们从最下面的一层Instrumentation Level(监控层)开始讲解。


监测(Instrumentation)

        回顾上一节,我们使用JMX可以监控服务器内存、CPU,可以监控JVM的堆内存、线程数等指标。监控的含义就体现在这,我们可以使用JMX来监控任何我们关心的性能指标。

        Oracle官方文档将监控各项指标称之为“监控资源”,个人认为,用“监控指标”可能更具有说服力。所以后文在提到“监控指标”或者“监控资源”的时候,其实都是一个含义。 以下为JMX官方文档关于监控的讲解的中文翻译:   

        “要想利用JMX技术来管理资源,你首先必须用Java编程语言来监测(instrument)该资源。你使用叫做MBeans的Java对象来实现对该资源的监测信息的访问。”

翻译成大白话就是:

        我们对关心的各项性能指标的监控,都是通过称之为MBean的Java对象来实现的。我们可以用一个MBean来监控操作系统的基本信息,用一个MBean来监控JVM的信息,用一个MBean监控应用程序....。


总结:监控层的作用就是使用MBean来监控我们关心的性能指标。因为我们通常关注的性能指标比较多,通常情况下,在监控层我们会有多个MBean,每个MBean监控一类信息。


JMX Agent(代理)

        我们已经知道,在监控层通常会有很多的MBean,用于监控不同的指标。一旦一个资源被MBeans监测,就可以通过一个JMX代理来管理它。

那么JMX代理是什么呢?

        你可以将JMX代理想象成一个容器,所有的MBean都注册到这个容器中。这个容器可以接受外部的请求,返回MBean的监控信息。当我们想从远程获取某个MBean的检测信息,我们就给这个容器发送一个请求,由这个容器将这个MBean的检测信息返回给我们。

        这很像一个Web服务器的作用,通常一个Web服务器中可以有多个Servlet,我们想请求某个Servlet时,只要指定URL,由WEB服务器来调用对应的Servlet方法,并将结果返回。所不同的是,JMX代理是内嵌在我们的Java应用程序中的,而不是像Tomcat这样的Web容器,单独起一个JVM进程来运行。


以下还是官方的一段介绍:        

MBeans不需要知道它们将要操作的JMX代理的信息。
MBeans设计目标为灵活、简单并方便实现。应用程序、系统和网络开发者可以以标准的方式让其产品成为可管理的产品,而不必理解或投资复杂的管理系统。可以以很小的代价将已有的资源改造成可管理的。

这段话,同样可以类比Web容器进行理解:我们写的Sevlet可以运行在Tomcat中,也可以运行在其他的Web容器中,如Jetty、Weblogic等。这些Web容器的实现方式可能不同,但是我们的Servlet都可以运行,因为这些WEB容器都实现了Servlet规范。对于JMX而言,而在不同版本JDK中,可能JMX代理的实现方式有所不同,但是只要其实现了JMX规范,我们的MBean都可以运行。


容器的作用,只是JMX代理最为核心的功能。在JMX中,我们将这种容器称之为MBean Server。

        JMX Agent的核心组件是MBean server,它是一个被管理对象的服务器,MBeans在其中注册。一个JMX代理还包括一组用于管理MBeans的服务和至少一个通信适配器(adaptor)或连接器(connector) 以供管理程序访问

        如果对比WEB容器的作用的话,相信这段话你也很容易就理解了。


最后再来一段废话,当然这段废话不是我说的,能说出这种废话的,当然是官方文档:  

  
        当你实现一个JMX代理的时候,你不需要知道它将会管理的资源的语义和功能。实际上,JMX Agent甚至不需要知道它将会服务哪些资源,因为任何资源,只要其监测兼容JMX规范,就能够使用任何JMX Agent为这些资源提供它们所需要的服务。类似地,JMX代理不需要知道将要访问它的管理程序的功能。

还是类比WEB容器,一个WEB容器可以运行不同功能的应用,你可以在上面跑一个电商网站,也可以是一个博客系统。对于JMX而言,不管你写什么MBean,来监控什么指标,  反正JMX代理上都能跑。      


远程管理(adaptors/connectors client)

        通常情况下,我们会用使用一个远程客户端连接MBean Server来获取MBean的监控信息。道理很简单,方便查看和管理。一方面,通过远程客户端,我们可以更好的查看检测到的信息,例如将监控信息在浏览器中展现出来,甚至可以做图表进行展现变化趋势;更为重要的是,企业中通常会有很多运行在不同服务器上的应用,每个应用都需要监控,我们可以编写一个远程客户端,这个远程客户端可以连接到所有要监控的服务器。

       

       JMX 可以以多重方式来访问JMX技术监测信息,既可以通过现有的管理协议,比如简单网络管理协议(SNMP),也可以通过专利性的协议。MBean server依赖协议适配器(adaptors)和连接器(connectors)来让JMX代理供管理程序(位于JMX代理所在的JVM之外)访问。
每个适配器都通过一个特定的协议提供一个包含了所有注册在MBean Server中的MBeans的视图。比如,一个HTML适配器可以在一个浏览器中显示一个MBean。

        连接器提供一个 管理器侧的 接口,该接口处理管理程序和JMX Agent之间的通信。不同的连接器 通过不同的协议 提供相同的远程管理接口。当一个远程管理程序使用该接口时,它可以通过网络透明地连接到一个JMX Agent,而不论该协议是什么。JMX技术提供一个标准的方案,用于导出基于JMX技术的监测信息,供基于Java RMI的应用程序使用。



2.2 Java虚拟机的监控和管理

JMX技术也可以用于监控和管理Java虚拟机。
        Java虚拟机具有内置的监测功能,使得你能够通 过JMX技术来监控和管理它。这些内置的功能通常叫做JVM的拆箱即用式的(out-of-the-box)管理工具。为了监控和管理JVM的不同方 面,JVM包含了一个平台MBean server以及一些特殊的MXBeans,供遵守JMX规范的管理程序使用。


Platform MXBeans和PlatformMBean Server

        platform MXBeans 是由Java SE平台提供的一组MXBeans,用于监控和管理JVM和Java Runtime Environment (JRE)的其它组件。每个platform MXBean都封装了一部分JVM功能,比如class-loading system系统、just-in-time (JIT)编译系统、垃圾回收器等等。可 以通过使用一个兼容JMX规范的监控与管理工具来显示这些MXBeans,以及与这些MXBeans进行交互,这样使得你能够监控和管理不同的虚拟机功 能。其中一个这样的监控与管理工具就是Java SE平台的JConsole graphical user interface (GUI)。

        Java SE platform提供了一个标准的platform MBean server,这些platform MXBeans就注册在其中。平台MBean server也可以注册任何其它你想要创建的MBeans。(提示: JVM启动的时候,会启动platform MBean server!应用程序可以启动自己的MBean Server,也可以使用JVM默认启动的platfomr MBean Server)


测试案例:

       
       
  1. public class HelloJMX {
  2.     public static void main(String[] args) throws InterruptedException {
  3.         Thread.sleep(Long.MAX_VALUE);
  4.     }
  5. }

运行此程序


JConsole

        Java SE平台包含了JConsole这个监控与管理工具,它兼容JMX规范。JConsole使用JVM的extensive instrumentation(也就是platform MXBeans)来提供运行于Java平台中的应用程序的性能与资源消耗信息。

Out-of-the-Box Management in Action

        因为标准的实现了JMX技术的监控和管理功能是内置于Java SE平台中的,因此你可以直接使用它们,而不需要写一行与JMX API相关的代码。你可以启动一个Java应用程序,然后用JConsolel来监控它。

用JConsole监控应用程序

        该过程展示了如何监控Notepad Java程序。在版本6之前的Java SE平台中,需要通过用下面的选项 来启动 你想要用JConsole进行监控的应用程序。

       
       
  1. -Dcom.sun.management.jmxremote

然而,Java SE 6 平台提供的那个版本的JConsole可以粘附(attach)到任何支持粘附API的本地应用程序上。换言之,任何在Java SE 6 HotSpot VM中启动的应用程序 都会被JConsole自动探测到,不需要用上面的命令行选项来启动该应用程序。

1、启动Jconsole

QQ截图20160119235216.png

输入jconsole之后,可以看到:

QQ截图20160120000242.png

2、在新连接对话框中,在本地进程(Local Process)列表中,选择com.tianshouzhi.jmx.HelloJMX,然后点击 Connect 按钮

    当JConsole打开的时候,呈现在你眼前的是关于该HelloJMX的监控与管理的一个概览。比如,你可以查看该应用程序所消耗的heap memory的数量、当前应用程序的线程数量 以及该应用程序所消耗的CPU量。

QQ截图20160120000614.png   

3. 点击JConsole中的不同标签页。
每个标签页都展示了HelloJMX所处的JVM的不同功能领域的更详细的信息。所有这些展示的信息,都是通过不同的JMX MXBeans获取,本教程已经提到过。所有的platform MXBeans都被显示在MBeans标签页中。例如在Thread Tab页中,我们看到Main线程:

QQ截图20160120000827.png    

4. 要想关闭JConsole,请选择 Connection -> Exit。



3 MBeans介绍

本课程介绍JMX API,也就是 managed beans或 MBeans 的基本概念。

一个MBean是一个被管理的Java对象,类似一个JavaBeans组件,它遵守JMX规范中的设计模式。一个MBean可以代表一个设备、一个应用程序或者任何需要被管理的资源。MBeans暴露一个管理接口,包括下面的内容:

    一组可读或可写的属性,或者两者都有。
    一组可调用的操作。
    一个自我描述。

在一个MBean instance的整个生命中,其管理接口不变。当预定义的事件发生时,MBeans也可以发出通知。

JMX规范定义了5种MBean:

    标准MBeans
    动态MBeans
    Open MBeans
    Model MBeans
    MXBeans

本教程中的例子只展示最简单的MBeans类型,也就是 标准MBeans MXBeans

3.1 标准(Standard)MBeans

本节展示一个直接的、标准的MBean的一个例子。

        一个标准的MBean的定义:编写一个叫做 HelloMBean 的Java接口 以及 一个实现该接口的、叫做 Hello 的Java类。在该接口中,每个方法要么定义该MBean的一个属性,要么定义该MBean的一个操作。默认情况下,每个方法都定义一个操作。属性和操作 是遵守某种设计模式的方法。一个标准的MBean由一个MBean接口(该MBean接口列出了所有被暴露的属性和操作对应的方法)和一个class(这 个class实现了这个MBean接口并提供被监测资源的功能)组成。

下面的章节展示了一个标准MBean例子,以及管理该MBean的一个简单的 JMX agent。

MBean接口

一个基本的MBean接口的例子,HelloMBean,如下:

       
       
  1. package com.tianshouzhi.jmx.mbean;
  2.  
  3. public interface HelloMBean { 
  4.  
  5.     public void sayHello(); 
  6.     public int add(int x, int y); 
  7.     
  8.     public String getName(); 
  9.      
  10.     public int getCacheSize(); 
  11.     public void setCacheSize(int size); 
  12. }

        习惯上,一个MBean接口的名字 由实现它的Java类的名字加上后缀 MBean 组成。在这里,接口叫做HelloMBean。实现该接口的类 Hello 在下一节描述。

        根据JMX规范,一个MBean接口由属性(可读的,可能也是可写的)和操作(可以由应用程序调用)组成。

1、属性:HelloMBean 声明了两个属性:Name是一个只读字符串,CacheSize 是一个可读并可写的整数。该接口声明了 Getter 和 setter 方法,以供管理程序访问和修改属性值。如JMX规范所定义,一个 getter 方法是任何 public 方法,不能返回void,其名字以get开始。getter 方法使得管理程序能够阅读该属性的值,其类型是返回对象的类型。setter 方法是任何public方法,带有一个唯一的参数,其名字以set开始。setter方法使得管理程序能够将一个新值写入该属性,其类型和参数的类型相同。

2、操作:HelloMBean 接口声明了两个操作:Java方法 add()sayHello()

这些操作和属性的实现将在下节展示。

MBean实现

下面的 Hello Java 类实现了 HelloMBean MBean 接口,需要注意的是根据JMX规范,实现类必须接口名去掉MBean,因此这里的实现类就是Hello:

       
       
  1. package com.tianshouzhi.jmx.mbean;
  2.  
  3. public class Hello implements HelloMBean {
  4.     public void sayHello() {
  5.         System.out.println("hello, world");
  6.     }
  7.  
  8.     public int add(int x, int y) {
  9.         return x + y;
  10.     }
  11.  
  12.     public String getName() {
  13.         return this.name;
  14.     }
  15.  
  16.     public int getCacheSize() {
  17.         return this.cacheSize;
  18.     }
  19.  
  20.     public synchronized void setCacheSize(int size) {
  21.         this.cacheSize = size;
  22.         System.out.println("Cache size now " + this.cacheSize);
  23.     }
  24.  
  25.     private final String name = "Reginald";
  26.     private int cacheSize = DEFAULT_CACHE_SIZE;
  27.     private static final int DEFAULT_CACHE_SIZE = 200;
  28. }

这个直接的 Hello 类提供了声明在HelloMBean中的操作和属性的定义。sayHello()和add()操作非常简单,但是现实生活中,根据需要,操作可以很简单也可以非常复杂。

读取Name属性的方法以及读写CacheSize属性的方法也都定义了。在这个例子中,Name属性的值永远不会改变。然而,在实际场景中,该属性可能会随着被管理资源的运行而改变。比如,该属性可能代表的是 诸如 运行时间和内存使用量 之类 的统计数据。这里,该属性只是"Reginald"这个名字。

通过调用 setCacheSize 方法,你可以改变CacheSize属性的默认值200。在实际的场景中,CacheSize属性可能需要其它操作来读写,比如抛弃一些条目或分配新条目的操作。这个例子只是打印一条消息,以确认缓存大小已经被改变了。然而,我们可以定义更复杂的操作,而不是简单地调用println()

在 Hello MBean及其接口定义好之后,就可以用它们来管理它们所代表的资源了。

创建一个JMX Agent来管理一个资源

一旦一个资源已经由MBeans监测,就可以通过JMX agent来管理该资源。

JXM agent的核心组件是MBean server。一个MBean server是被管理对象的一个服务器,MBeans就注册在其中。一个JMX agent也包括一组管理MBeans的服务。关于MBean Server实现的细节,请参见 MBeanServer 接口的API文档。

下面的 Main class代表了一个基本的JMX agent:

       
       
  1. package com.tianshouzhi.jmx.mbean;
  2.  
  3. import java.lang.management.*;
  4. import javax.management.*;
  5.  
  6. public class Main {
  7.  
  8.     public static void main(String[] args) throws Exception {
  9.  
  10.         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
  11.         ObjectName name = new ObjectName("com.tianshouzhi.jmx.mbean:type=Hello");
  12.         Hello mbean = new Hello();
  13.         mbs.registerMBean(mbean, name);
  14.  
  15.         System.out.println("Waiting forever...");
  16.         Thread.sleep(Long.MAX_VALUE);
  17.     }
  18. }

        这个JMX agent,Main,首先通过调用java.lang.management.ManagementFactory类的getPlatformMBeanServer()方法,来获取一个MBean server,该MBean Serer已经由Java SE平台创建并初始化。如果平台还没创建MBean server,那么 getPlatformMBeanServer()就会 通过调用JMX方法MBeanServerFactory.createMBeanServer(),自动地创建一个MBean Server。由Main获取的MBeanServer instance被叫做mbs。

        接着,Main 为 将要被创建的MBean instance 定义了一个对象名。每个JMX MBean都必须有一个对象名。对象名是JMX类ObjectName 的一个实例,必须遵守定义在JMX规范中的语法。也就是说,对象名必须包含一个domain以及一个"键-值"属性列表。由Main定义的这个对象名中,domain是com.example (包含这个MBean例子的包名)。此外,"键-值"属性 声明了该对象的类型是为Hello

        接着创建了Hello对象的一个实例,命名为mbean。然后,通过将这个叫做mbean的Hello对象 及其 对象名传入JMX方法MBeanServer.registerMBean(),该Hello对象,就被注册为MBean Server mbs中的一个MBean,其对象名为name

        当该 Hello MBean 注册到了该 MBean server中之后,Main简单地等待对Hello进行管理操作。在这个例子中,这些管理操作是 调用sayHello()add()方法 以及读写属性的值。

标准MBean的优势和局限:

  • 局限:操作方法的参数以及返回值的类型 只能是基本类型,比如String、int、long等等。

  • 优势:上面这个局限,正是SOA架构中,各Service松耦合所需要的!各MBean完全没有reference依赖!这种优势正是来自这个局限。

我 们也可以发现,通过JConsole操作MBean时,参数只能是基本类型!复杂类型是无法表述的!如果我们用了java.util.Date这样的复杂 类型,则JConsole只能读取,不能设置!如果我们在Getter/Setter中使用了自定义的类型(JDK中不存在),则 JConsole(Java SE7版)提示"不可用",因为JConsole无法识别它。

运行这个标准MBean例子

你已经查看了这个例子的相关classe,你现在可以运行该例子了。在本例中,我们使用JConsole来和该MBean进行交互。

要想运行这个例子,遵照下面的步骤:

    如果你用的是 JDK 6,那么用下面的命令启动这个Main应用程序。

       
       
  1. java com.tianshouzhi.jmx.mbean.Main

   
    如果你用的JDK版本老于 版本6,那么你需要用下面指定的选项来启动该 Main 程序,从而暴露该程序中的MBean接口以供监控和管理。

       
       
  1.  java -Dcom.sun.management.jmxremote com.tianshouzhi.jmx.mbean.Main

   
    启动程序后,在终端窗口中启动JConsole。

    jconsole

    新连接对话框就会被心事,展示一列运行中的、可供你连接的JMX agents。
    在新连接对话框中,从列表中选择 com.tianshouzhi.jmx.mbean.Main,然后点击Connect。你的平台的当前活动的一个汇总信息就会被现实出来。
    点击MBeans标签页。该版面现实了所有注册在该MBean Server中的MBeans。在左边的边框中,展开MBean树中的 com.tianshouzhi.jmx.mbean节点。
QQ截图20160122221056.png    

    你可以看见我们的样例MBean,Hello,它已经被创建,并由Main注册。如果你点击Hello,你就能在MBean树中,看见其相关的属性和操作节点。在这个界面中,你可以进行以下操作


    展开Hello MBean在MBean树中的属性节点。

    由Hello类定义的MBean属性就会被现显示出来。
    将CacheSize属性的值改为150。

    在你启动Main的终端窗口中,你将看到一条关于该属性的修改确认信息被生成。
    展开Hello MBean在MBean树中的操作节点。

    由Hello MBean声明的两个操作 sayHello() 和 add()就会被显示出来。
    通过点击sayHello按钮来调用 sayHello() 操作。

    一个JConsole对话框将会通知帮你,该方法被调用成功。在Main所运行的终端窗口中,"hello, world"消息被生成。
    为 add()操作提供两个整数,然后点击 add 按钮。

    答案将会显示在一个JConsole对话框中。
    要想关闭JConsole,选择 Connection -> Exit。


3.2 MXBeans

本节解释一种特殊的MBean,叫做MXBeans用于将JDK携带的类型组合为自定义的复杂类型

        MXBean 是一种MBean,它只引用预定义的一组数据类型(例如java.util.Date)。这样,你就能够确保你的MBean能够被任何客户端使用,包括远程客户端,而不需要客户端访问 代表你的MBeans的、和模型相关的classes。MXBeans提供了一种方便的途径,来将相关值绑定到一起,而不需要额外配置客户端来处理相关 classes包。

        和标准MBeans的做法一样,一个MXBean的定义为:编写一个叫做 SomethingMXBean 的接口 以及 实现该接口的一个Java类。然而,MXBeans不像标准MBeans,MXBeans不要求Java类必须叫做Something。在该接口中的每个方法,定义的要么是该MXBean的一个属性,要么是该MXBean的一个操作。也可以用注解@MXBean来标注Java接口,从而不需要接口的名字以"MXBean"为后缀。

        MXBeans存在于 Java 2 Platform, Standard Edition (J2SE) 5.0中,位于 java.lang.management 包下。然而,除了定义在java.lang.management中的标准MXBeans集合之外,用户现在可以定义他们自己的MXBeans。

        MXBeans背后的主要思想是,在MXBean接口中使用的类型,比如java.lang.management.MemoryUsage 被映射到一组标准的类型,也就是定义在javax.management.openmbean包中的、所谓的"Open Types"。确切的映射规则定义在MXBean规范中。然而,常规原则是,对于诸如int或string之类简单的类型,保持不变;而诸如MemoryUsage之类的复杂类型,就被映射到标准类型CompositeDataSupport

MXBean例子包含下面的文件,可以在jmx_examples.zip中找到:

  • QueueSamplerMXBean 接口

  • QueueSampler 类,实现了 MXBean接口。

  • QueueSample Java类型,由MXBean接口中的 getQueueSample()方法返回。

  • Main,搭建和运行这个例子的程序。

这个MXBean例子使用这些类来执行下面的动作:

  • 定义一个简单的MXBean,用于管理一个Queue<String>类型的资源。

  • 在MXBean中声明一个getter,getQueueSample,当调用该getter时,它对该队列做快照并返回一个Java类QueueSample,这个QueueSample将下面的值绑定在一起:

    • 做快照的时间

    • 队列的大小

    • 在做快照时,队列的头

  • 在一个MBean Server中注册该 MXBean

       

MXBean接口

下面的代码展示了例子QueueSamplerMXBean的MXBean接口:

       
       
  1. package com.example; 
  2.  
  3. public interface QueueSamplerMXBean { 
  4.  public QueueSample getQueueSample(); 
  5.  public void clearQueue(); 
  6. }

注意,你声明一个MXBean接口的方式和声明一个标准的MBean接口的方式完全相同。QueueSamplerMXBean接口声明了一个getter,getQueueSample ,以及一个操作clearQueue

定义MXBean操作

QueueSampler是MBean实现,QueueSample是一个自定义的JavaBean!别混淆了。

MXBean操作声明在 QueueSampler类中,如下:

       
       
  1. package com.example; 
  2.  
  3. import java.util.Date; 
  4. import java.util.Queue; 
  5.  
  6. public class QueueSampler 
  7.  implements QueueSamplerMXBean { 
  8.  
  9.  private Queue<String> queue; 
  10.  
  11.  public QueueSampler (Queue<String> queue) { 
  12.  this.queue = queue; 
  13.  } 
  14.  
  15.  public QueueSample getQueueSample() { 
  16.  synchronized (queue) { 
  17.  return new QueueSample(new Date(), 
  18.   queue.size(), queue.peek()); 
  19.  } 
  20.  } 
  21.  
  22.  public void clearQueue() { 
  23.  synchronized (queue) { 
  24.  queue.clear(); 
  25.  } 
  26.  } 
  27. }

QueueSampler 定义了 getQueueSample() 这个getter 以及 clearQueue()操作,它们已经声明在MXBean接口中了。getQueueSample()操作返回一个 QueueSample 类型的实例,这个实例由 java.util.Queue 的peek()和 size()方法返回的值 以及 一个 java.util.Date 实例 构成。

定义MXBean接口返回的Java类型

QueueSampler返回的QueueSample实例定义在 QueueSample 中,如下:

       
       
  1. package com.example; 
  2.  
  3. import java.beans.ConstructorProperties; 
  4. import java.util.Date; 
  5.  
  6. public class QueueSample { 
  7.  
  8.  private final Date date; 
  9.  private final int size; 
  10.  private final String head; 
  11.  
  12.  @ConstructorProperties({"date", "size", "head"}) 
  13.  public QueueSample(Date date, int size, 
  14.   String head) { 
  15.  this.date = date; 
  16.  this.size = size; 
  17.  this.head = head; 
  18.  } 
  19.  
  20.  public Date getDate() { 
  21.  return date; 
  22.  } 
  23.  
  24.  public int getSize() { 
  25.  return size; 
  26.  } 
  27.  
  28.  public String getHead() { 
  29.  return head; 
  30.  } 
  31. }

在 QueueSample 中,MXBean框架会调用QueueSample中的所有getters,并将返回的实例转化为一个 CompositeData 实例,然后使用@ConstructorProperties注解 从一个CompositeData实例中重构一个 QueueSample 实例。

在MBean Server中创建并注册MXBean

到目前位置,我们已经定义了:一个MXBean接口以及该接口的实现类,要返回的Java类型。接着必须创建该MXBean,并将其注册到MBean Server中。这些动作由我们在标准MBean例子中使用的同一个 Main 例子来执行,但是,相关代码在 Standard MBean 课程中并没用显示。

       
       
  1. package com.example; 
  2.  
  3. import java.lang.management.ManagementFactory; 
  4. import java.util.Queue; 
  5. import java.util.concurrent.ArrayBlockingQueue; 
  6. import javax.management.MBeanServer; 
  7. import javax.management.ObjectName; 
  8.  
  9. public class Main { 
  10.  
  11.  public static void main(String[] args) throws Exception { 
  12.  MBeanServer mbs = 
  13.  ManagementFactory.getPlatformMBeanServer(); 
  14.  
  15.  ... 
  16.  ObjectName mxbeanName = new ObjectName("com.example:type=QueueSampler");
  17.  
  18.  Queue<String> queue = new ArrayBlockingQueue<String>(10);
  19.  queue.add("Request-1");
  20.  queue.add("Request-2");
  21.  queue.add("Request-3");
  22.  QueueSampler mxbean = new QueueSampler(queue);
  23.  
  24.  mbs.registerMBean(mxbean, mxbeanName);
  25.   
  26.  System.out.println("Waiting..."); 
  27.  Thread.sleep(Long.MAX_VALUE); 
  28.  } 
  29. }

这个 Main 执行下面的动作:

  • 得到 platform MBean server。

  • 为MXBean QueueSampler创建一个对象名。

  • 创建一个 Queue 实例,供 QueueSampler MXBean来处理。

  • 将该Queu实例喂给新创建的 QueueSampler MXBean。

  • 在MBean Server中注册该MXBean,和注册标准MBean方式完全相同。

Running the MXBean Example(和运行MBean类似,注意下面的第10步关于组合类型的描述)

Running the MXBean Example(和运行MBean类似,注意下面的第10步关于组合类型的描述)

这个MXBean例子使用了来自 jmx_examples.zip 包的classes,你已经在 Standard MBeans 章节使用过这个包了。该例子要求Java SE 6平台。要想运行这个MXBeans例子,请遵循下面的步骤:

   1、 如果你还没将jmx_examples.zip保存到你的work_dir目录,那么请保存一下。
   2、在终端窗口中,用下面的命令解压例子包。

       
       
  1.     unzip jmx_examples.zip


   3、 在work_dir目录下,编译样例Java类:

    javac com/example/*.java

    4、启动 Main 程序。一条等待什么事情发生的确认消息就会被生成。

    java com.example.Main

    5、在相同及其上的不同终端窗口中,启动JConsole。新连接对话框就会被显示,它列举了运行中的、你可以连接的JMX agents。

    jconsole

    6、在这个新连接对话框中,从上述列表中选择com.example.Main,然后点击 Connect。

    你的Java SE平台(也就是运行Main程序的JVM)当前活动的一个汇总就会被显示出来。
    7、点击 MBeans 标签页。

        这个面板显示了当前注册在MBean Server中的所有的MBeans。
    8、 在左边的边框中,展开MBean树中的 com.example 节点。

    你可以看见由Main创建并注册的样例MBean QueueSampler。如果你点击 QueueSampler,你就会在MBean树中看到与之相关的属性和操作的节点。
    9、展开Attributes节点。

    你会看到 QueueSample 属性出现在右边的边框中,其值为javax.management.openmbean.CompositeDataSupport。
    双击 CompositeDataSupport 值。

   10、 你将看到 QueueSample 的值:date、head和size,因为MXBean框架已经将 QueueSample 实例转化成了 CompositeData。如果你将QueueSampler定义为标准的MBean,而不是MXBean,那么JConsole就无法找到QueueSample类,因为这个类没用出现在JConsole的class path中。如果 QueueSampler 被定义为标准MBean,当你读取QueueSample属性的值的时候,你就会得到一个ClassNotFoundException消息【Java SE 7携带的JConsole会显示"不可用",而不是抛出ClassNotFoundException】。JConsole能够找到QueueSampler这个事实表明:当通过诸如JConsole之类的常规JMX客户端连接到JMX agents的时候,MXBeans很有用。
   11、 展开Operations节点

   12、 将会显示一个出发 clearQueue 操作的按钮。
    点击 clearQueue 按钮。

   13、 该方法被成功调用的确认消息就会被显示出来。
    再次展开 Attributes 节点,双击 CompositeDataSupport 值。

    14、head 和 size 的值已经被重设了。
    要想关闭JConsole,选择 Connection -> Exit。
 小结:

    JMX规范要想确保:

        对所有JMX MBean的操作都禁止直接使用这些MBean的reference
        任何满足JMX规范的、常规的JMX客户端工具都能够访问JMX Agents,

    那么只能对MBean中各方法的参数和返回值的类型做严格限制,这些类型只能是基本类型(String,int,double,float...)以及JMX规范指定的CompositeDataSupport(用于组合基本类型)。稍微复杂一点的类型,比如java.util.Date,就算我们为之定义了public的getter和setter,在显示的时候getter显示的是java.util.Date.toString();然而无法通过setter修改,因为JMX Client工具不知道如何将我们的字符串输入转化成java.util.Date。


4 通知

JMX API定义了一种机制,使得MBeans能够生成通知Notificationn,比如通知一个状态改变、一个检测到的事件或者问题。通知的作用是主动通知远程客户端。例如我们的程序出现了异常,或者CPU使用率过高,或者程序出现了死锁等一系列问题。这个时候我们希望程序能主动将这些问题发送给远程客户端,将这些问题记录下来,或者执行一些其他的报警操作。

通知的类型

在JMX中,通知通过类 javax.management.Notification来表示。因为通知有很多种,所以Notification有很多子类,用于细分不同类型的通知,如下:
QQ截图20160123175654.png

可以看到细分的通知种类有很多,最容易理解的是 AttributeChangedNotification。在前面我们已经讲解过MBean中可以定义属性和操作,表示的就是当属性发生变化时,我们可以发出的通知类型。
       每个通知都有一个源头。源头就是产生该通知的MBean的对象名。一个MBean只要检测条件的变化,就可以发送对应的通知。
       每个通知都有一个序列号编号。当通知的接收顺序很重要 并且 以错误的顺序处理通知可能会有风险时,该编号可以用于对来自相同通知源头的通知进行排序。序列编号可以是0,但是为每个来自特定MBean的通知 设置 递增的号码 会更好。举例来说,看一下AttributeChangedNotification的构造方法:

       
       
  1. /**
  2.      * Constructs an attribute change notification object.
  3.      * In addition to the information common to all notification, the caller must supply the name and type
  4.      * of the attribute, as well as its old and new values.
  5.      *
  6.      * @param source The notification producer, that is, the MBean the attribute belongs to.
  7.      * @param sequenceNumber The notification sequence number within the source object.
  8.      * @param timeStamp The date at which the notification is being sent.
  9.      * @param msg A String containing the message of the notification.
  10.      * @param attributeName A String giving the name of the attribute.
  11.      * @param attributeType A String containing the type of the attribute.
  12.      * @param oldValue An object representing value of the attribute before the change.
  13.      * @param newValue An object representing value of the attribute after the change.
  14.      */
  15. public AttributeChangeNotification(Object source, long sequenceNumber, long timeStamp, String msg,
  16.                                        String attributeName, String attributeType, Object oldValue, Object newValue)

source指的是生成这个通知的MBean实例,sequenceNumber指的是通知的序号。


通知的发送

很容易想到,既然我们MBean可以发送通知,那么对应的,肯定有一个通知的接受方。不过目前我们不讨论通知的接收方,只考虑如何发送通知。
     通常情况下,通知的发送方和通知的接收方是不在同一台物理机器上的,所以发送通知这个操作必然是需要进行网络传输的,而且我们还要监听通知的产生等等方面的问题。幸运的是,在JMX中,我们并不需要进行网络编程来发送通知。在JMX中,有一个NotificationBroadcasterSupport类,其已经帮助我们实现了发送一个通知需要考虑的各种问题:监听通知的产生、通知的发送等...。我们的MBean只需要继承这个类,就可以具备发送通知的功能。

一个MBean要想生成通知,那么它必须实现接口 NotificationEmitter 或者扩展 NotificationBroadcasterSupport。我们要关注的是以下两个方法:

       
       
  1. public class NotificationBroadcasterSupport implements NotificationEmitter {
  2.     ...
  3.     public MBeanNotificationInfo[] getNotificationInfo(){...}
  4.     public void sendNotification(Notification notification){...}
  5.     ...
  6. }

sendNotification方法:

发送一条通知,你需要构造javax.management.Notification或其子类(比如AttributeChangedNotification)的一个实例传入即可。

getNotificationInfo方法:

        前面提到的Notification的子类有很多种,这个方法是告诉JMX框架我们的MBean会发送哪几种类型的通知。如果我们在MBean中发送的通知类型,但是没有在这里声明,那么就是非法的。
        这个方法的返回值是MBeanNotificationInfo[],每个通知类型对应数组中的一个元素。MBeanNotificationInfo对象中维护的信息包括:通知类型,通知对应的java类的全名,以及一些描述信息等。

通知的实现:

Standard MBeans课程中描述的Hello MBean实现实际上已经实现了通知机制。只是,为了简便起见,相关代码在该课程中省略了。 Hello 的完整代码如下:

       
       
  1. package com.tianshouzhi.jmx.notification;
  2.  
  3. import javax.management.AttributeChangeNotification;
  4. import javax.management.MBeanNotificationInfo;
  5. import javax.management.Notification;
  6. import javax.management.NotificationBroadcasterSupport;
  7. public class Hello extends NotificationBroadcasterSupport implements HelloMBean {
  8.  
  9.     public void sayHello() {
  10.         System.out.println("hello, world");
  11.     }
  12.  
  13.     public int add(int x, int y) {
  14.         return x + y;
  15.     }
  16.  
  17.     public String getName() {
  18.         return this.name;
  19.     }
  20.  
  21.     public int getCacheSize() {
  22.         return this.cacheSize;
  23.     }
  24.  
  25.     public synchronized void setCacheSize(int size) {
  26.         int oldSize = this.cacheSize;
  27.         this.cacheSize = size;
  28.  
  29.         System.out.println("Cache size now " + this.cacheSize);
  30.         //构建通知
  31.         Notification n = new AttributeChangeNotification(this,
  32.                 sequenceNumber++, System.currentTimeMillis(),
  33.                 "CacheSize changed", "CacheSize", "int", oldSize,
  34.                 this.cacheSize);
  35.         //发送通知
  36.         sendNotification(n);
  37.     }
  38.  
  39.     //返回这个MBean将会发送的通知类型信息
  40.     @Override
  41.     public MBeanNotificationInfo[] getNotificationInfo() {
  42.         String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE };
  43.         String name = AttributeChangeNotification.class.getName();
  44.         String description = "An attribute of this MBean has changed";
  45.         MBeanNotificationInfo info = new MBeanNotificationInfo(types, name,
  46.                 description);
  47.         return new MBeanNotificationInfo[] { info };
  48.     }
  49.  
  50.     private final String name = "Reginald";
  51.     private int cacheSize = DEFAULT_CACHE_SIZE;
  52.     private static final int DEFAULT_CACHE_SIZE = 200;
  53.  
  54.     private long sequenceNumber = 1;
  55. }

Hello MBean扩展了NotificationBroadcasterSupport类。NotificationBroadcasterSupport 实现了 NotificationEmitter 接口。

各个操作和各个属性的设置方式和标准MBean那个例子相同,唯一的例外是CacheSize的属性的setter方法现在定义了一个 oldSize值。该值记录了在对CacheSize属性执行set操作之前,CacheSize属性的值。

通知从JMX class AttributeChangeNotification构造而来,名字为 n,AttributeChangeNotification扩展了javax.management.Notification。我们在方法setCacheSize()中构造该通知,携带下列信息。该信息被作为参数传给 AttributeChangeNotification

  • 通知源头的对象名,也就是 Hello MBean,用this代表它。

  • 一个序列号,也就是sequenceNumber,它被设置为1,然后逐渐递增。

  • 一个时间戳。

  • 通知消息的内容。

  • 被改变的属性的名字,在这里,为CacheSize

  • 被改变的属性的类型。

  • 被改变的属性的旧值,在这里,为oldSize

  • 被改变的属性的新值,在这里,为this.cacheSize

该通知,n,然后被传递给 NotificationBroadcasterSupport.sendNotification() 方法。

最后,定义了 MBeanNotificationInfo 实例,它用于描述该MBean为特定类型的通知 生成的不同通知的特征。

关于MBeanNotificationInfo的提示

"public MBeanNotificationInfo[] getNotificationInfo()"方法来自顶层接口 javax.management.MBeanNotificationInfo[] getNotificationInfo()。只在注册该MBean的时候,调用一次,用于获取该MBean 为特定类型的通知  生成的不同通知的特征。

如果想要接收某种类型的消息,那么需要到 MBean Server中订阅(JMX Client也可以到MBean Server中订阅)。

本例中,该方法实现为:

       
       
  1. public MBeanNotificationInfo[] getNotificationInfo() {
  2.     String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE };
  3.     String name = AttributeChangeNotification.class.getName();
  4.     String description = "An attribute of this MBean has changed";
  5.     //
  6.     MBeanNotificationInfo info = new MBeanNotificationInfo(
  7.             types,
  8.             name,
  9.             description);
  10.     return new MBeanNotificationInfo[] { info };
  11. }

这个方法只体现了一个类型的通知,AttributeChangeNotification.ATTRIBUTE_CHANGE。没有体现出多种类型的通知。实际应用中,该方法可能是这样的:

       
       
  1. public MBeanNotificationInfo[] getNotificationInfo() {
  2.     String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE, XXX”, "YYY" ... };
  3.     String name = AttributeChangeNotification.class.getName();
  4.     String description = "An attribute of this MBean has changed";
  5.     //
  6.     MBeanNotificationInfo info1 = new MBeanNotificationInfo(
  7.             types,
  8.             name,
  9.             description);
  10.     MBeanNotificationInfo info2 = new MBeanNotificationInfo(...)
  11.     return new MBeanNotificationInfo[] { info1, info2, info3, ... };
  12. }

运行该MBean Notification例子

1、运行程序。

2、使用JConsole连接。查看MBeans标签页。

QQ截图20160123182545.png

可以看到名称为Hello的MBean多了一个Notifications。

3、点击Subscribe按钮,表示要订阅通知。

4、修改CacheSize的值

QQ截图20160123182729.png

修改之后点击refresh按钮。

5、刷新之后,我们可以看到Notifications[0]变成了Notifications[1],表示接受到了一条消息。


5 远程管理

  JMX API使得你能够通过基于JMX技术的连接器(JMX连接器:JMX connectors)对你资源进行远程管理。JMX connector使得MBean server能够被基于Java的远程客户端访问。连接器的客户端所导出的接口和就是MBean server中MBean的接口。

        一个JMX连接器包括:connector clientconnector serverconnector server 被粘附到MBean server上,它会监听来自客户端的连接。connector client负 责建立与connector server之间的连接。connector client所在的JVM通常与connector server所在的JVM不同,而且通常位于不同的机器上。JMX API 定义了一个标准的连接协议,它是基于Remote Method Invocation (RMI)的。该协议使得你能够 远程地 将JMX client连接到一个注册在MBean Server中的MBean上,然后操作该MBean,和在本地操作完全一样。

        Java SE通过JMX API's standard RMI connector为远程监控程序提供了开箱即用的途径。开箱即用的RMI connector自动地暴露应用程序(提示:指的是JMX Agent所处的程序),用于远程管理,不需要你亲自创建具体的remote connector server。只要你在启动你的Java程序(提示:指的是MBean Agent所处的程序)时设置了正确的系统属性,开箱即用的remote management agent就会被激活。兼容JMX技术的监控与管理程序 然后就能够连接到这些程序(YBXIANG:指的是MBean Agent所处的程序)上,然后远程监控他们。


5.1 通过JConsole暴露一个资源以供远程管理

如果你使用开箱即用的 remote management agent 和现有的诸如JConsole之类的监控与管理工具,那么利用JMX API暴露你的Java应用程序用于远程管理就变得非常简单。

要想暴露你的应用程序(YBXIANG:指的是MBean Agent所在的程序)以便远程管理,你需要用正确的系统属性启动它。这个例子展示了如何暴露 Main JMX agent 以供远程管理。

安全考虑: 

为了简便起见,在本例中,禁掉了认证和加密安全机制。然而,当你在现实环境中实现远程管理时,你应该实现这些安全机制。What Next? 提供了其它JMX技术文档的参考,展示了如何激活安全。

This example requires version 6 of the Java SE platform. To monitor the Main JMX agent remotely, follow these steps:

    If you have not done so already, save jmx_examples.zip into your work_dir directory.
    Unzip the bundle of sample classes by using the following command in a terminal window.

    unzip jmx_examples.zip

    Compile the example Java classes from within the work_dir directory.

    javac com/example/*.java

    Start the Main application, specifying the properties that expose Main for remote management:

    java -Dcom.sun.management.jmxremote.port = 9999 \
     -Dcom.sun.management.jmxremote.authenticate = false \
     -Dcom.sun.management.jmxremote.ssl = false \
     com.example.Main

    A confirmation that Main is waiting for something to happen is generated.
    Start JConsole in a different terminal window on a different machine:

    jconsole

    The New Connection dialog box is displayed, presenting a list of running JMX agents that you can connect to locally.
    Select Remote Process, and type the following in the Remote Process field:

    hostname:9999

    In this address, hostname is the name of the remote machine on which the Main application is running and 9999 is the number of the port on which the out-of-the-box JMX connector will be connected.
    Click Connect.

    A summary of the current activity of the Java Virtual Machine (Java VM) in which Main is running is displayed.
    Click the MBeans tab.

    This panel shows all the MBeans that are currently registered in the remote MBean server.
    In the left-hand frame, expand the com.example node in the MBean tree.

    You see the example MBean Hello that was created and registered by Main. If you click Hello, you see its associated Attributes and Operations nodes in the MBean tree, even though it is running on a different machine.
    To close JConsole, select Connection -> Exit.


5.2 创建一个客户化的JMX客户端

本教程的前几堂课程已经想你展示了如何创建 JMX MBeans和MXBeans以及如何在JMX Agent中注册它们。然而,所有前面的例子使用的都是现有的JMX 客户端 - JConsole。这堂课将展示如何创建我们自己的客户化的JMX客户端。

一个客户化的JMX客户端,Client,包含在jmx_examples.zip中。该JMX客户端和前面课程中介绍的的同样的MBean, MXBean以及JMX agent进行交互。由于Client比较大,我们将在下面章节中逐块地查看它。

导入JMX Remote API类

要想创建从JMX Client到远程JMX Agents的连接,你需要使用来自 javax.management.remote 这个包的类。

       
       
  1. package com.example;
  2. ...
  3.  
  4. import javax.management.remote.JMXConnector;
  5. import javax.management.remote.JMXConnectorFactory;
  6. import javax.management.remote.JMXServiceURL;
  7.  
  8. public class Client {
  9. ...

Client类需要一个JMXConnectorFactory和一个JMXServiceURL来创建 JMXConnector 实例。

Creating a Notification Listener

JMX client需要一个通知处理器,用于监听并处理由注册在JMX agent的MBean Server中的MBeans发出的任何通知。JMX client的通知处理器是NotificationListener接口的一个实例,如下所示:

       
       
  1. ... 
  2.  
  3. public static class ClientListener implements NotificationListener {
  4.  
  5.  public void handleNotification(Notification notification,
  6.  Object handback) {
  7.  echo("\nReceived notification:");
  8.  echo("\tClassName: " + notification.getClass().getName());
  9.  echo("\tSource: " + notification.getSource());
  10.  echo("\tType: " + notification.getType());
  11.  echo("\tMessage: " + notification.getMessage());
  12.  if (notification instanceof AttributeChangeNotification) {
  13.  AttributeChangeNotification acn =
  14.  (AttributeChangeNotification) notification;
  15.  echo("\tAttributeName: " + acn.getAttributeName());
  16.  echo("\tAttributeType: " + acn.getAttributeType());
  17.  echo("\tNewValue: " + acn.getNewValue());
  18.  echo("\tOldValue: " + acn.getOldValue());
  19.  }
  20.  }
  21. } 
  22. ...

这个通知监听器判定它所收到的通知的来源,提取存储在该通知中的信息。然后根据收到的通知的类型,对通知信息做不同的处理。在这里,当监听器收到 AttributeChangeNotification 类型的通知时,它将通过调用AttributeChangeNotificationgetAttributeName、getAttributeTypegetNewValue和getOldValue方法,获取 已经被改变的MBean属性的名字和类型,以及新值和旧值。

然后创建一个新的 ClientListener 实例。

       
       
  1. ClientListener listener = new ClientListener();

创建一个RMI Connector Client

这个Client类创建了一个RMI connector client,我们对它进行配置以便连接到RMI connector server上,当你启动JMX Agent Main 的时候就会启动该RMI connector server。这样做使得这个JMX客户端能够和JMX agent进行交互,就仿佛两者运行在相同的机器上一样。

       
       
  1. ...
  2.  
  3. public static void main(String[] args) throws Exception {
  4.  
  5. echo("\nCreate an RMI connector client and " +
  6.  "connect it to the RMI connector server");
  7. JMXServiceURL url = 
  8.  new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
  9.  JMXConnector jmxc = JMXConnectorFactory.connect(url, null);//只是连接到RMI Registry!还没用连接到MBean Server。...

如你所见,Client定义了一个JMXServiceURL,命名为url,它代表着 connector client 期望找到的connector server的位置。该URL让connector client能够从 运行在本地主机的9999端口上的RMI注册机 获取 RMI connector server的stub,命名为jmxrmi,然后连接到RMI connector server。【提示: 如果connector client和connector server处于不同的机器上,那么需要用new JMXServiceURL("service:jmx:rmi:///jndi/rmi://${CONNECTOR_SERVER_IP}:9999 /jmxrmi")。MBean Server如果用RMI Connector,那么应该可以用"java.rmi.server.hostname"来设置RMI注册机的IP地址,参见:Start RMI Server using Specific IP Address。】

这样定义RMI注册机之后,就能够创建connector client了。connector client,命名为jmxc,是JMXConnector接口的一个实例,通过JMXConnectorFactoryconnect()方法创建。该connect()方法在创建的时候,被传入了参数 url 以及一个空的环境map。

连接到远程MBean Server

RMI connection就位之后,JMX client必须连接到远程MBean server,这样它才能够 通过远程JMX agent 和注册在该远程MBean server中的各个MBeans进行交互。

       
       
  1. ...
  2.  
  3. MBeanServerConnection mbsc = 
  4.  jmxc.getMBeanServerConnection();
  5.  
  6. ...

通过调用JMXConnector实例jmxc的getMBeanServerConnection()方法,就能够创建 MBeanServerConnection 的一个实例,命名为mbsc。

connector client现在已经连接到由JMX agent创建的MBean server上了,这样,它就能够注册MBeans并对MBeans执行操作了,该连接对于两端来说,都是完全透明。

首先,客户端定义一些简单的操作,以便发现注册在该agent的MBean Server之中的MBeans的信息。

       
       
  1. ...
  2.  
  3. echo("\nDomains:");
  4. String domains[] = mbsc.getDomains();
  5. Arrays.sort(domains);
  6. for (String domain : domains) {//打印所有domains名字
  7.  echo("\tDomain = " + domain);
  8. }
  9.  
  10. ...
  11.  
  12. echo("\nMBeanServer default domain = " + mbsc.getDefaultDomain());
  13.  
  14. echo("\nMBean count = " + mbsc.getMBeanCount());
  15. echo("\nQuery MBeanServer MBeans:");
  16. Set<ObjectName> names = 
  17.  new TreeSet<ObjectName>(mbsc.queryNames(null, null));
  18. for (ObjectName name : names) {
  19.  echo("\tObjectName = " + name);
  20. }
  21.  
  22. ...

该客户端调用 MBeanServerConnection 的各种方法,以便获取不同MBeans所属的domains、注册在MBean Server中的MBeans的数量 以及 该客户端所发现的各个MBeans的对象名。

       


通过代理(Proxies)对远程MBeans执行操作

客户端创建注册在MBean Server中的Hello MBean的一个proxy,通过MBean server connection来访问该MBean。该MBean proxy位于客户端,它模拟了远程MBean。

       
       
  1. ...
  2.  
  3. ObjectName mbeanName = new ObjectName("com.example:type=Hello");HelloMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, 
  4.  HelloMBean.class, true);//true:通知MBean Server: 该client proxy需要接收通知。echo("\nAdd notification listener...");
  5.  mbsc.addNotificationListener(mbeanName, listener, null, null);
  6.  echo("\nCacheSize = " + mbeanProxy.getCacheSize());mbeanProxy.setCacheSize(150);echo("\nWaiting for notification...");
  7. sleep(2000);echo("\nCacheSize = " + mbeanProxy.getCacheSize());echo("\nInvoke sayHello() in Hello MBean...");
  8. mbeanProxy.sayHello();echo("\nInvoke add(2, 3) in Hello MBean...");
  9. echo("\nadd(2, 3) = " + mbeanProxy.add(2, 3));
  10.  
  11. waitForEnterPressed();
  12.  
  13. ...

MBean proxies使得你能够通过一个Java接口来访问一个MBean,让你能够对该proxy进行调用,而不是写很长的代码(YBXIANG:指的是纯粹的JMX风格的代码)来访问一个远程MBean。这里,我们通过调用javax.management.JMX类的newMBeanProxy()方法,传入参数"MBean's MBeanServerConnection、object name、MBean接口的类名 以及 true(该参数意味着该proxy必须像一个NotificationBroadcaster那样)",为Hello创建了一个MBean proxy。现在,JMX client就能够执行由Hello定义的操作了,就好像注册在本地的MBean的操作。该JMX client也添加了一个通知监听器,当修改MBean的CacheSize属性的时候,该MBean就会发出一个通知。

通过代理(Proxies)对远程MXBeans执行操作

你可以为MXBeans创建代理,和创建MBeans代理完全一样。

       
       
  1. ...
  2.  
  3. ObjectName mxbeanName = new ObjectName ("com.example:type=QueueSampler");
  4. QueueSamplerMXBean mxbeanProxy = JMX.newMXBeanProxy(mbsc, 
  5.  mxbeanName, QueueSamplerMXBean.class);QueueSample queue1 = mxbeanProxy.getQueueSample();//可以返回组合对象!
  6.  echo("\nQueueSample.Date = " + queue1.getDate());
  7. echo("QueueSample.Head = " + queue1.getHead());
  8. echo("QueueSample.Size = " + queue1.getSize());
  9. echo("\nInvoke clearQueue() in QueueSampler MXBean...");
  10. mxbeanProxy.clearQueue();QueueSample queue2 = mxbeanProxy.getQueueSample();echo("\nQueueSample.Date = " + queue2.getDate());
  11. echo("QueueSample.Head = " + queue2.getHead());
  12. echo("QueueSample.Size = " + queue2.getSize());
  13.  
  14. ...

如上所示,要想为MXBean创建一个代理,你所需要做的一切只是调用 JMX.newMXBeanProxy,而不是newMBeanProxy。这个MXBean代理,mxbeanProxy,使得客户端能够触发 QueueSampler MXBean的操作,就好像注册在本地的MXBean的操作一样。
关闭连接

一旦JMX客户端获取到了它所需要的一切信息,并对位于远程JMX agent的MBean Server中的MBeans执行了所有的、必须的操作之后,就必须关闭相关连接。

    jmxc.close();

通过调用JMXConnector.close方法来关闭该连接。


To Run the Custom JMX Client Example

This example requires version 6 of the Java SE platform. To monitor the Main JMX agent remotely using a custom JMX client Client, follow these steps:

    If you have not done so already, save jmx_examples.zip into your work_dir directory.
    Unzip the bundle of sample classes by using the following command in a terminal window.

    unzip jmx_examples.zip

    Compile the example Java classes from within the work_dir directory.

    javac com/example/*.java

    Start the Main application, specifying the properties that expose Main for remote management:

    java -Dcom.sun.management.jmxremote.port=9999 \
     -Dcom.sun.management.jmxremote.authenticate=false \
     -Dcom.sun.management.jmxremote.ssl=false \ 
     com.example.Main

    A confirmation that Main is waiting for something to happen is generated.
    Start the Client application in a different terminal window:

    java com.example.Client

    A confirmation that an MBeanServerConnection has been obtained is displayed.
    Press Enter.

    The domains in which all the MBeans that are registered in the MBean server started by Main are displayed.
    Press Enter again.

    The number of MBeans that are registered in the MBean server is displayed, as well as the object names of all these MBeans. The MBeans displayed include all the standard platform MXBeans running in the Java VM, as well as the Hello MBean and the QueueSampler MXBean that were registered in the MBean server by Main.
    Press Enter again.

    The Hello MBean's operations are invoked by Client, with the following results:
        A notification listener is added to Client to listen for notifications from Main.
        The value of the CacheSize attribute is changed from 200 to 150.
        In the terminal window in which you started Main, confirmation of the CacheSize attribute change is displayed.
        In the terminal window in which you started Client, a notification from Main is displayed, informing Client of the CacheSize attribute change.
        The Hello MBean's sayHello operation is invoked.
        In the terminal window in which you started Main, the message "Hello world" is displayed.
        The Hello MBean's add operation is invoked, with the values 2 and 3 as parameters. The result is displayed by Client.
    Press Enter again.

    The QueueSampler MXBean's operations are invoked by Client, with the following results:
        The QueueSample values date, head, and size are displayed.
        The clearQueue operation is invoked.
    Press Enter again.

    The Client closes the connection to the MBean server and a confirmation is displayed.


6 教程末尾

你已经阅读到"Java Management Extensions (JMX)"这份教程的末尾了。

如果你有关于本教程的评论和建议,请使用我们的 反馈页面 告诉我们。

本教程的目的是为你提供JMX技术基本元素的一个简单介绍。更高级的材料,可以在别的地方找到。

如果你想要进一步学习JMX技术,下面的资源将会很有用:

 

除了出现在上面教程中的例子之外,在你安装好JDK 6之后,可以在下面的目录中找到一个样例程序,它展示了现实生活中JMX API的一个实现:

       
       
  1. jdk_home/sample/jmx/jmx-scandir YBXIANG:本站分析jdk-8u20-windows-x64-demos.zip - JMX

其中,jdk_home是JDK的安装目录。这个jmx-scandir例子是一个高级的例子,它描述了实际生活场景中的JMX API的高级概念。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值