从零开始玩转JMX(一)——简介和Standard MBean

JMX的全称为Java Management Extensions. 顾名思义,是管理Java的一种扩展。这种机制可以方便的管理、监控正在运行中的Java程序。常用于管理线程,内存,日志Level,服务重启,系统环境等。

简介

基本术语

  • MBean:是Managed Bean的简称,可以翻译为“管理构件”。在JMX中MBean代表一个被管理的资源实例,通过MBean中暴露的方法和属性,外界可以获取被管理的资源的状态和操纵MBean的行为。事实上,MBean就是一个Java Object,同JavaBean模型一样,外界使用自醒和反射来获取Object的值和调用Object的方法,只是MBean更为复杂和高级一些。MBean通过公共方法以及遵从特定的设计模式封装了属性和操作,以便暴露给管理应用程序。例如,一个只读属性在管理构件中只有Get方法,既有Get又有Set方法表示是一个可读写的属性。一共有四种类型的MBean: Standard MBean, Dynamic MBean, Open MBean, Model MBean。
  • MBeanServer:MBean生存在一个MBeanServer中。MBeanServer管理这些MBean,并且代理外界对它们的访问。并且MBeanServer提供了一种注册机制,是的外界可以通过名字来得到相应的MBean实例。
  • JMX Agent:Agent只是一个Java进程,它包括这个MBeanServer和一系列附加的MbeanService。当然这些Service也是通过MBean的形式来发布。
  • Protocol Adapters and Connectors:MBeanServer依赖于Protocol Adapters和Connectors来和运行该代理的Java虚拟机之外的管理应用程序进行通信。Protocol Adapters通过特定的协议提供了一张注册在MBeanServer的MBean的视图。例如,一个HTML Adapter可以将所有注册过的MBean显示在Web 页面上。不同的协议,提供不同的视图。Connectors还必须提供管理应用一方的接口以使代理和管理应用程序进行通信,即针对不同的协议,Connectors必须提供同样的远程接口来封装通信过程。当远程应用程序使用这个接口时,就可以通过网络透明的和代理进行交互,而忽略协议本身。Adapters和Connectors使MBean服务器与管理应用程序能进行通信。因此,一个代理要被管理,它必须提供至少一个Protocol Adapter或者Connector。面临多种管理应用时,代理可以包含各种不同的Protocol Adapters和Connectors。当前已经实现和将要实现的Protocol Adapters和Connectors包括: RMI Connector, SNMP Adapter, IIOP Adapter, HTML Adapter, HTTP Connector.

Adapter 和Connector的区别在于:Adapter是使用某种Internet协议来与JMX Agent获得联系,Agent端会有一个对象 (Adapter)来处理有关协议的细节。比如SNMP Adapter和HTTP Adapter。而Connector则是使用类似RPC的方式来访问Agent,在Agent端和客户端都必须有这样一个对象来处理相应的请求与应答。比如RMI Connector。

JMX Agent可以带有任意多个Adapter,因此可以使用多种不同的方式访问Agent。

JMX基本构架

JMX分为三层,分别负责处理不同的事务。它们分别是:

  • Instrumentation 层
    Instrumentation层主要包括了一系列的接口定义和描述如何开发MBean的规范。通常JMX所管理的资源有一个或多个MBean组成,因此这个资源可以是任何由Java语言开发的组件,或是一个JavaWrapper包装的其他语言开发的资源。
  • Agent 层
    Agent 用来管理相应的资源,并且为远端用户提供访问的接口。Agent层构建在Intrumentation层之上,并且使用并管理 Instrumentation层内部描述的组件。Agent层主要定义了各种服务以及通信模型。该层的核心是一MBeanServer,所有的MBean都要向它注册,才能被管理。注册在MBeanServer上的MBean并不直接和远程应用程序进行通信,他们通过协议适配器(Adapter)和连接器(Connector)进行通信。通常Agent由一个MBeanServer和多个系统服务组成。JMX Agent并不关心它所管理的资源是什么。
  • Distributed 层
    Distributed层关心Agent如何被远端用户访问的细节。它定义了一系列用来访问Agent的接口和组件,包括Adapter和Connector的描述。

如果一个Java对象可以由一个遵循JMX规范的管理器应用管理,那么这个Java对象就可以由JMX管理资源。要使一个Java对象可管理,则必须创建相应的MBean对象,并通过这些MBean对象管理相应的Java对象。当拥有MBean类后,需要将其实例化并注册到MBeanServer上。


详述

这里采用的是JDK7,JDK7中已经包含了jmx,但是如果用到HtmlAdaptorServer类(后面会看到)还需要用到jmxtools.jar, 可以去这里下载,有两个包:jmx-1_2_1-ri.zip; jmx_remote-1_0_1_03-ri.zip。jmx-1_2_1-ri.zip解压后lib中有jmxri.jar和jmxtools.jar,将jmxtool.jar拷贝出来放入classpath中即可(jmxri.jar在JDK5+已经包被包含了)。

Standard MBean

Standard MBean的设计和实现是最简单的,它们的管理接口通过方法名来描述。Standard MBean的实现依靠一组命名规则,称之为设计模式。这些命名规则定义了属性和操作。
检查Standard MBean接口和应用设计模式的过程被称为内省(Introspection)。JMX代理通过内省来查看每一个注册在MBeanServer上的MBean的方法和超类,看它是否遵从一定设计模式,决定它是否代表了一个MBean,并辨认出它的属性和操作。
Standard MBean是JMX管理构件中最简单的一种,只需要开发一个MBean接口(为了实现Standard MBean,必须遵循一套继承规范。必须每一个MBean定义一个接口,而且这个接口的名字必须是其被管理的资源的对象类的名称后面加上”MBean”),一个实现MBean接口的类,并且把它们注册到MBeanServer中就可以了。

package com.test.jmx;

public interface HelloMBean {
    public String getName();
    public void setName(String name);
    public void printHello();
    public void printHello(String whoName);
}

接下来是真正的资源对象,因为命名规范的限制,因此对象名称必须为Hello.

package com.test.jmx;

public class Hello implements HelloMBean {
    private String name;
    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void printHello() {
        System.out.println("Hello world, "+ name);
    }

    @Override
    public void printHello(String whoName) {
        System.out.println("Hello, "+whoName);
    }
}

接下去创建一个Agent类:

package com.test.jmx;

import com.sun.jdmk.comm.HtmlAdaptorServer;

import javax.management.*;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class HelloAgent {
    public static void main(String[] args) throws MalformedObjectNameException,
            NotCompliantMBeanException, InstanceAlreadyExistsException,
            MBeanRegistrationException, IOException {
        // 下面这种方式不能再JConsole中使用
//      MBeanServer server = MBeanServerFactory.createMBeanServer();
// 首先建立一个MBeanServer,MBeanServer用来管理我们的MBean,通常是通过MBeanServer来获取我们MBean的信息,间接
// 调用MBean的方法,然后生产我们的资源的一个对象。
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();

        String domainName = "MyMBean";

//为MBean(下面的new Hello())创建ObjectName实例
        ObjectName helloName = new ObjectName(domainName+":name=HelloWorld");
// 将new Hello()这个对象注册到MBeanServer上去
        mbs.registerMBean(new Hello(),helloName);

// Distributed Layer, 提供了一个HtmlAdaptor。支持Http访问协议,并且有一个不错的HTML界面,这里的Hello就是用这个作为远端管理的界面
// 事实上HtmlAdaptor是一个简单的HttpServer,它将Http请求转换为JMX Agent的请求
        ObjectName adapterName = new ObjectName(domainName+":name=htmladapter,port=8082");
        HtmlAdaptorServer adapter = new HtmlAdaptorServer();
        adapter.start();
        mbs.registerMBean(adapter,adapterName);

        int rmiPort = 1099;
        Registry registry = LocateRegistry.createRegistry(rmiPort);

        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:"+rmiPort+"/"+domainName);
        JMXConnectorServer jmxConnector = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
        jmxConnector.start();
    }
}

编译运行,在浏览器中输入localhost:8082,这样我们就可以对程序进行管理,如图:

可以看到我们注册的MyMBean域下的”name=HelloWorld”,可以点击进去,然后可以修改属性Name和执行2个printHello方法,可以在控制台看到效果。具体不贴图赘述,机智的小伙伴一试就知道怎么玩转了。

上面代码中还通过RMI(JMXServiceURL, JMXConnectorServer )注册URL来提供客户端连接,可以通过JConsole作为客户端来管理MBean. 打开JConsole工具(%JAVA_HOME%/bin/jconsole.exe),如图在远程进程中输入rmi地址“service:jmx:rmi:///jndi/rmi://localhost:1099/MyMBean”:

点击“连接”之后就出现:

这样就可以像HTML一样管理MBean了。

注意上面的代码中:

Registry registry = LocateRegistry.createRegistry(rmiPort);

可以在某一特定端口创建名字服务,从而用户无需再手工启动rmiregistry,如果不加入这句代码,就会出现Connection Refused的异常:

Exception in thread "main" java.io.IOException: Cannot bind to URL [rmi://localhost:1099/MyMBean]: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
    java.net.ConnectException: Connection refused: connect]
    at javax.management.remote.rmi.RMIConnectorServer.newIOException(Unknown Source)
    at javax.management.remote.rmi.RMIConnectorServer.start(Unknown Source)
    at com.test.jmx.HelloAgent.main(HelloAgent.java:44)
Caused by: javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
    java.net.ConnectException: Connection refused: connect]
    at com.sun.jndi.rmi.registry.RegistryContext.bind(Unknown Source)
    at com.sun.jndi.toolkit.url.GenericURLContext.bind(Unknown Source)
    at javax.naming.InitialContext.bind(Unknown Source)
    at javax.management.remote.rmi.RMIConnectorServer.bind(Unknown Source)
    ... 2 more
Caused by: java.rmi.ConnectException: Connection refused to host: localhost; nested exception is:
    java.net.ConnectException: Connection refused: connect
    at sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
    at sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
    at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
    at sun.rmi.server.UnicastRef.newCall(Unknown Source)
    at sun.rmi.registry.RegistryImpl_Stub.bind(Unknown Source)
    ... 6 more
Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
    at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
    at java.net.PlainSocketImpl.connect(Unknown Source)
    at java.net.SocksSocketImpl.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at java.net.Socket.connect(Unknown Source)
    at java.net.Socket.<init>(Unknown Source)
    at java.net.Socket.<init>(Unknown Source)
    at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(Unknown Source)
    at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(Unknown Source)
    ... 11 more

当然,这也就其他的解决办法:运行 %JAVA_HOME%/bin/rmiregistry.exe 1099就有和那行代码一样的效果。

我们不仅可以通过JConsole作为客户端采用rmi的方式来进行管理,我们同样可以采用自定义程序作为客户端来连接JMXConnectorServer管理MBean.

package com.test.jmx;

import java.io.IOException;
import java.util.Iterator;
import java.util.Set;

import javax.management.Attribute;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class Client {

    public static void main(String[] args) throws IOException,
            MalformedObjectNameException, InstanceNotFoundException,
            AttributeNotFoundException, InvalidAttributeValueException,
            MBeanException, ReflectionException, IntrospectionException {
        String domainName = "MyMBean";
        int rmiPort = 1099;
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:"+rmiPort+"/"+domainName);
        // 可以类比HelloAgent.java中的那句:
        // JMXConnectorServer jmxConnector = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
        JMXConnector jmxc = JMXConnectorFactory.connect(url);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

        //print domains
        System.out.println("Domains:------------------");
        String domains[] = mbsc.getDomains();
        for(int i=0;i<domains.length;i++){
            System.out.println("\tDomain["+i+"] = "+domains[i]);
        }
        //MBean count
        System.out.println("MBean count = "+mbsc.getMBeanCount());
        //process attribute
        ObjectName mBeanName = new ObjectName(domainName+":name=HelloWorld");
        mbsc.setAttribute(mBeanName, new Attribute("Name","zzh"));//注意这里是Name而不是name
        System.out.println("Name = "+mbsc.getAttribute(mBeanName, "Name"));

        //接下去是执行Hello中的printHello方法,分别通过代理和rmi的方式执行
        //via proxy
        HelloMBean proxy = MBeanServerInvocationHandler.newProxyInstance(mbsc, mBeanName, HelloMBean.class, false);
        proxy.printHello();
        proxy.printHello("jizhi boy");
        //via rmi
        mbsc.invoke(mBeanName, "printHello", null, null);
        mbsc.invoke(mBeanName, "printHello", new String[]{"jizhi gril"}, new String[]{String.class.getName()});

        //get mbean information
        MBeanInfo info = mbsc.getMBeanInfo(mBeanName);
        System.out.println("Hello Class: "+info.getClassName());
        for(int i=0;i<info.getAttributes().length;i++){
            System.out.println("Hello Attribute:"+info.getAttributes()[i].getName());
        }
        for(int i=0;i<info.getOperations().length;i++){
            System.out.println("Hello Operation:"+info.getOperations()[i].getName());
        }

        //ObjectName of MBean
        System.out.println("all ObjectName:--------------");
        Set<ObjectInstance> set = mbsc.queryMBeans(null, null);
        for(Iterator<ObjectInstance> it = set.iterator();it.hasNext();){
            ObjectInstance oi = it.next();
            System.out.println("\t"+oi.getObjectName());
        }
        jmxc.close();
    }
}

运行结果:

Domains:------------------
    Domain[0] = MyMBean
    Domain[1] = java.nio
    Domain[2] = JMImplementation
    Domain[3] = com.sun.management
    Domain[4] = java.lang
    Domain[5] = java.util.logging
MBean count = 21
Name = zzh
Hello Class: com.test.jmx.Hello
Hello Attribute:Name
Hello Operation:printHello
Hello Operation:printHello
all ObjectName:--------------
    java.lang:type=OperatingSystem
    java.lang:type=Compilation
    java.lang:type=MemoryPool,name=PS Old Gen
    java.lang:type=Memory
    JMImplementation:type=MBeanServerDelegate
    java.lang:type=MemoryPool,name=PS Perm Gen
    java.lang:type=Runtime
    MyMBean:name=htmladapter,port=8082
    java.nio:type=BufferPool,name=direct
    java.lang:type=GarbageCollector,name=PS MarkSweep
    java.nio:type=BufferPool,name=mapped
    java.lang:type=Threading
    com.sun.management:type=HotSpotDiagnostic
    java.lang:type=GarbageCollector,name=PS Scavenge
    MyMBean:name=HelloWorld
    java.lang:type=ClassLoading
    java.lang:type=MemoryPool,name=PS Survivor Space
    java.lang:type=MemoryManager,name=CodeCacheManager
    java.lang:type=MemoryPool,name=Code Cache
    java.util.logging:type=Logging
    java.lang:type=MemoryPool,name=PS Eden Space

这是客户端的运行结果,由于在客户端调用了服务端的方法,可以在服务端看到打印结果:

Hello world, zzh
Hello, jizhi boy
Hello world, zzh
Hello, jizhi gril

上面代码涉及到辅助原数据的概念:辅助元数据类用来描述管理构件。辅助元数据类不仅被用来内省标准管理构件,也被动态管理构件用来进行自我描述。这些类根据属性、操作、构建器和通告描述了管理接口。JMX代理通过这些元数据类管理所有管理构件,而不管这些管理构件的类型。部分辅助元类如下:

  1. MBeanInfo–包含了属性、操作、构建器和通知的信息。
  2. MBeanFeatureInfo–为下面类的超类。
  3. MBeanAttributeInfo–用来描述管理构件中的属性。
  4. MBeanConstructorInfo–用来描述管理构件中的构建器。
  5. MBeanOperationInfo–用来描述管理构件中的操作。
  6. MBeanParameterInfo–用来描述管理构件操作或构建器的参数。
  7. MBeanNotificationInfo–用来描述管理构件发出的通知。

有关Notication, Dynamic MBean, Model MBean以及Apache Common Modeler由于篇幅限制将后面文章中讲述。


参考资料

  1. JMX整理
  2. JMX简介
  3. http://blog.csdn.net/DryKillLogic/article/category/762777
  4. 用Apache的commons-modeler来辅助开发JMX
时间: 2024-07-28 13:53:07

从零开始玩转JMX(一)——简介和Standard MBean的相关文章

从零开始玩转JMX(三)——Model MBean

Model MBean 相对于Standard MBean,Model MBean更加灵活.如果我们不能修改已有的Java类,那么使用Model MBean是不错的选择. Model MBean也是一种专门化的动态管理构件.它是预制的.通用的和动态的 MBean 类,已经包含了所有必要缺省行为的实现,并允许在运行时添加或覆盖需要定制的那些实现.JMX规范规定该类必须实现为javax.management.modelmbean.RequiredModelMBean,管理者要做的就是实例化该类,并配

从零开始玩转JMX(二)——Condition

Notification 一个MBean提供的管理接口允许代理对其管理资源进行控制和配置.然而,对管理复杂的分布式系统来说,这些接口知识提供了一部分功能.通常,管理应用程序需要对状态变化或者当特别情况发生变化时作出反映.Notification起到了MBean之间的沟通桥梁的作用.JMX Notification模型和Java Event模型类似,将一些重要的信息,状态的转变,数据的变更传递给Notification Listener,以便资源的管理. 通知模型仅仅涉及了在同一个JMX代理中的管

从零开始玩编程系列:文件捆绑器

  将多个文件合并成一个最终可执行文件,运行这个最终合成文件后,就相当于运行了合并前的多个文件,这种程序在木马或后门程序合并中会经常用到,你想知道它是怎么用程序实现的么?下面我就用VC6做的一个文件捆绑器的例子来告诉你. 其实文件捆绑器的构成思想非常简单:合并文件时,建立一个新的二进制文件,先写入你的自身捆绑程序的数据和文件长度,再写入你要捆绑的第一个文件的数据和其文件长度,跟着写入你要捆绑的第二个文件的数据和文件长度--最后直接写入你要捆绑的最后一个文件的数据(不需其文件长度).分解释放最终合

从零开始玩转logback

概述 LogBack是一个日志框架,它与Log4j可以说是同出一源,都出自Ceki Gülcü之手.(log4j的原型是早前由Ceki Gülcü贡献给Apache基金会的)下载地址:http://logback.qos.ch/download.html LogBack.Slf4j和Log4j之间的关系 Slf4j是The Simple Logging Facade for Java的简称,是一个简单日志门面抽象框架,它本身只提供了日志Facade API和一个简单的日志类实现,一般常配合Log

来玩Play框架01 简介

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!   说到网络框架,Ruby的Ruby on Rail和Python的Django都相当轻巧好用,但Java下的框架,则要沉重很多.有人因此质疑Java语言本身是否符合网络时代的需求.Java大神们对这一问题嗤之以鼻.想要轻巧好用的框架?写一个给你就是了.Java程序员Guillaume Bort在JVM上创造了一个全新的框架Play framework.Play拥有ROR或Dja

如何使用JMX监控Kafka

使用kafka做消息队列中间件时,为了实时监控其性能时,免不了要使用jmx调取kafka broker的内部数据,不管是自己重新做一个kafka集群的监控系统,还是使用一些开源的产品,比如yahoo的kafka manager, 其都需要使用jmx来监控一些敏感的数据.在kafka官网中 http://kafka.apache.org/082/documentation.html#monitoring 这样说: Kafka uses Yammer Metrics for metrics repo

jmx的简单学习

背景 前段时间在看btrace源码和jdk一些源码的时候,经常会看到一些jmx的相关内容.以前对jmx基本是一片空白区,花了点时间学习记录下.   过程 jmx总体架构图:  说明:  1.  Agent : javax.management.MBeanServer实现了Agent的功能,以标准的方式给出了管理系统访问 JMX 框架的接口 2. SubAgent: javax.management.MBeans实现了SubAgent的功能,以标准的方式给出了 JMX 框架访问资源的接口 MBea

扩展 Spring 的 JMX 支持(1)

Spring 框架将体系结构依赖性降至最低,并且将应用程序中得组成部分进行了具体化,但是应用程序仍然是需要管理的.幸运的是,Spring 1.2 包括高级的 JMX 集成支持,并且 JMX 为应用程序提供了一种实用的管理基础架构.在本文中,Claude Duguay 从 Spring JMX 更进一步,向您展示了如何为方法和属性透明地增加通知事件.最后得到的代码使您可以监视状态变化,同时不会搞乱 Java? 对象. 虽然 Spring 框架的 JMX 管理基础架构的默认配置已经很不错了,但是仍然

扩展Spring的JMX支持

Spring 框架将体系结构依赖性降至最低,并且将应用程序中得组成部分进行了具体化,但是应用程序仍然是需要管理的.幸运的是,Spring 1.2 包括高级的 JMX 集成支持,并且 JMX 为应用程序提供了一种实用的管理基础架构.在本文中,Claude Duguay 从 Spring JMX 更进一步,向您展示了如何为方法和属性透明地增加通知事件.最后得到的代码使您可以监视状态变化,同时不会搞乱 Java? 对象. 虽然 Spring 框架的 JMX 管理基础架构的默认配置已经很不错了,但是仍然