开源框架是如何通过JMX来做监控的(一) - JMX简介和Standard MBean

相关文章目录:

开源框架是如何通过JMX来做监控的(一) - JMX简介和Standard MBean

开源框架是如何通过JMX来做监控的(二) - Druid连接池的监控

相信很多做Java开发的同学都使用过JDK自带的 jconsole 或者 jvisualvm 监控过JVM的运行情况,但不知道有没有留意过它们会有一个MBean的功能/标签,通过MBean可以看到在JVM中运行的组件的一些属性和操作

例如,可以看到Tomcat 8080端口Connector的请求连接池信息,Druid数据库连接池的activeCount连接数以及连接池配置信息,这些开源框架或中间件都是通过JMX的方式将自己的一些管理和监控信息暴露给我们

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

下面简单介绍一下JMX和其最简单的Standard MBean,以及常用实例,之后的文章中会分析Druid数据库连接池等开源框架是如何通过JMX做监控的,没准在我们的程序中也可以用到。

以下是本文的目录大纲:

一、JMX架构及基本概念

二、Standard MBean

三、通过RMI方式连接JMX Server

四、通过VisualVM连接JMX Server

若有不正之处请多多谅解,欢迎批评指正、互相讨论。

请尊重作者劳动成果,转载请标明原文链接:

http://www.cnblogs.com/trust-freedom/p/6842332.html

一、JMX架构及基本概念

从上面的架构图可以看到JMX主要分三层,分别是:

1、设备层(Instrumentation Level)

主要定义了信息模型。在JMX中,各种管理对象以管理构件的形式存在,需要管理时,向MBean服务器进行注册。该层还定义了通知机制以及一些辅助元数据类。

设备层其实就是和被管设备通信的模块,对于上层的管理者来说,Instrumentation 就是设备,具体设备如何通信,是采用SNMP,还是采用ICMP,是MBean的事情。

该层定义了如何实现JMX管理资源的规范。一个JMX管理资源可以是一个Java应用、一个服务或一个设备,它们可以用Java开发,或者至少能用Java进行包装,并且能被置入JMX框架中,从而成为JMX的一个管理构件(Managed Bean),简称MBean。管理构件可以是标准的,也可以是动态的,标准的管理构件遵从JavaBeans构件的设计模式;动态的管理构件遵从特定的接口,提供了更大的灵活性。

在JMX规范中,管理构件定义如下:它是一个能代表管理资源的Java对象,遵从一定的设计模式,还需实现该规范定义的特定的接口。该定义了保证了所有的管理构件以一种标准的方式来表示被管理资源。

管理接口就是被管理资源暴露出的一些信息,通过对这些信息的修改就能控制被管理资源。一个管理构件的管理接口包括:

1) 能被接触的属性值

2) 能够执行的操作

3) 能发出的通知事件

4) 管理构件的构建器

本文着重介绍最基本也是用的最多的Standard Mbean,至于其它的如Dynamic MBean、Model MBean暂时不介绍,请参考本文最后资料中的文章。

Standard MBean是最简单的MBean,它管理的资源必须定义在接口中,然后MBean必须实现这个接口。它的命名也必须遵循一定的规范,例如我们的MBean为Hello,则接口必须为HelloMBean。

2、代理层(Agent Level)

Agent层 用来管理相应的资源,并且为远端用户提供访问的接口。Agent层构建在设备层之上,并且使用并管理设备层内部描述的组件。Agent层主要定义了各种服务以及通信模型。该层的核心是 MBeanServer,所有的MBean都要向它注册,才能被管理。注册在MBeanServer上的MBean并不直接和远程应用程序进行通信,他们通过 协议适配器(Adapter) 连接器(Connector) 进行通信。通常Agent由一个MBeanServer和多个系统服务组成。JMX Agent并不关心它所管理的资源是什么。

3、分布服务层(Distributed Service Level)

分布服务层关心Agent如何被远端用户访问的细节。它定义了一系列用来访问Agent的接口和组件,包括Adapter和Connector的描述。

二、Standard MBean

Standard MBean的设计和实现是最简单的,它们的管理接口通过方法名来描述。Standard MBean的实现依靠一组命名规则。这些命名规则定义了属性和操作。

一个只读属性在MBean中只有get方法,既有get又有set方法表示是一个可读写的属性。

为了实现Standard MBean,必须遵循一套继承规范。必须为每一个MBean定义一个接口,而且这个接口的名字必须是其被管理的资源的对象类的名称后面加上”MBean”,之后把它们注册到MBeanServer中就可以了

MBean接口:

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

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

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 String printHello() {
        return "Hello "+ name;
    }

    @Override
    public String printHello(String whoName) {
        return "Hello  " + whoName;
    }
}

接下来创建一个HelloMBean,并将其注册到MBeanServer:

public class HelloAgent {
    public static void main(String[] args) throws Exception {
        // 首先建立一个MBeanServer,MBeanServer用来管理我们的MBean,通常是通过MBeanServer来获取我们MBean的信息
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();  

       String domainName = "MyMBean";
        // 为MBean(下面的new Hello())创建ObjectName实例
        ObjectName helloName = new ObjectName(domainName+":name=HelloWorld");

        // 将new Hello()这个对象注册到MBeanServer上去
        server.registerMBean(new Hello(),helloName);
    }
}

三、通过RMI方式连接JMX Server

继续上面的HelloMBean、HelloAgent,我们可以通过RMI的方式注册URL来提供客户端连接,这样就可以通过 jvisualvm 或者 自己写的Java程序作为JMX的客户端来管理MBean

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

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;

public class HelloAgent {
	public static void main(String[] args) throws Exception {
		//create mbean server
		MBeanServer server = ManagementFactory.getPlatformMBeanServer();

		//create object name
		ObjectName objectName = new ObjectName("jmxBean:name=hello");

		//create mbean and register mbean
		server.registerMBean(new Hello(), objectName);

		/**
		 * JMXConnectorServer service
		 */
		//这句话非常重要,不能缺少!注册一个端口,绑定url后,客户端就可以使用rmi通过url方式来连接JMXConnectorServer
		Registry registry = LocateRegistry.createRegistry(1099);

		//构造JMXServiceURL
		JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
		//创建JMXConnectorServer
		JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, server);
		//启动
		cs.start();
	}
}

在创建JMXConnectorServer时创建的JMXServiceURL比较复杂,但其实其完整版为:

service:jmx:rmi://localhost:0/jndi/rmi://localhost:1099/jmxrmi

蓝色部分可以省略掉

service:jmx:    这个是JMX URL的标准前缀,所有的JMX URL都必须以该字符串开头,否则会抛MalformedURLException

rmi:    这个是jmx connector server的传输协议,在这个url中是使用rmi来进行传输的

localhost:0    这个是jmx connector server的IP和端口,也就是真正提供服务的host和端口,可以忽略,那么会在运行期间随意绑定一个端口提供服务

jndi/rmi://localhost:1099/jmxrmi    这个是jmx connector server的路径,具体含义取决于前面的传输协议。比如该URL中这串字符串就代表着该jmx connector server的stub是使用 jndi api 绑定在 rmi://localhost:1099/jmxrmi 这个地址


如果在服务器端,我们用该URL创建一个jmx connector server,则大概流程如下:

1、将jmx connect server内部的server对象的rmi stub export到本地的一个随机端口(也可以自己指定),接收外部连接

2、通过jndi api将该stub绑定在rmi://localhost:6000/jmxrmi这个地址上,这需要在本地的1099端口上运行着一个rmiregistry,如果不存在则会抛出异常


如果在客户端,我们通过该URL创建一个connector,则大概流程如下:

1、访问1099端口,通过jndi api到rmi://localhost:1099/jmxrmi这个地址取回stub

2、stub中已经包含了真实服务器的IP和端口,所以可以直接根据该stub连接到真实的服务器

下面具体展示一段客户端代码,可以获取jmx connector,并展示HelloMBean的一些信息,属性,及调用其方法

import java.util.Iterator;
import java.util.Set;
import javax.management.Attribute;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class JMXClient {
	public static void main(String[] args) throws Exception {
        //connect JMX
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        ObjectName mbeanName = new ObjectName("jmxBean:name=hello");     

        //print domains
        System.out.println("Domains:---------------");
        String domains[] = mbsc.getDomains();
        for (int i = 0; i < domains.length; i++) {
            System.out.println("Domain[" + i +"] = " + domains[i]);
        }
        System.out.println();

        //MBean count
        System.out.println("MBean count:---------------");
        System.out.println("MBean count = " + mbsc.getMBeanCount());
        System.out.println();

        //process attribute
        System.out.println("process attribute:---------------");
        mbsc.setAttribute(mbeanName, new Attribute("Name", "newName")); //set value
        System.out.println("Name = " + mbsc.getAttribute(mbeanName, "Name")); //get value
        System.out.println();

        //invoke via proxy
        System.out.println("invoke via proxy:---------------");
        HelloMBean proxy = (HelloMBean) MBeanServerInvocationHandler.newProxyInstance(mbsc, mbeanName, HelloMBean.class, false);
        System.out.println(proxy.printHello());
        System.out.println(proxy.printHello("zhangsan"));
        System.out.println();

        //invoke via rmi
        System.out.println("invoke via rmi:---------------");
        System.out.println(mbsc.invoke(mbeanName, "printHello", null, null));
        System.out.println(mbsc.invoke(mbeanName, "printHello", new Object[] { "lisi" }, new String[] { String.class.getName() }));
        System.out.println();

        //get mbean information
        System.out.println("get mbean information:---------------");
        MBeanInfo info = mbsc.getMBeanInfo(mbeanName);
        System.out.println("Hello Class:" + info.getClassName());
        System.out.println("Hello Attribute:" + info.getAttributes()[0].getName());
        System.out.println("Hello Operation:" + info.getOperations()[0].getName());
        System.out.println();

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

        jmxc.close();
    }
}

例子中我们可以get/set HelloMBean的Name属性,可以通过先获取代理和直接RMI的方式调用HelloMbean的printHello()方法等。

这样我们就有了一个完整的JMX server、client的例子,很多开源框架(如一些数据库连接池DBCP2、Druid)都实现了JMX来达到对其自身的监控,虽然这不是唯一的方法,也可以通过其它方式提供监控查询接口,但由于JMX是sun提出的通用标准,故大家纷纷响应实现。所以当我们使用这些开源框架并希望对其运行状况做一些管理监控时,可以采用JMX的方式获取其暴露出的MBean相关属性和方法。之后会分析一些Druid是如何通过JMX的方式做管理监控的。

四、通过VisualVM连接JMX Server

打开JDK自带的VisualVM,由于本例是本地localhost的JMX server,那么在左侧栏“本地”上右键,创建JMX连接

点击后只需填写本地的RMIRegistry注册的端口1099即可,当然也可以填写完整的URL:service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi

连接成功后,就可以看到HelloMBean以及其提供的属性和操作,还可以调用printHello()方法

参考资料:

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

JMX超详细解读

JMX架构的简单介绍

时间: 2024-08-14 04:58:03

开源框架是如何通过JMX来做监控的(一) - JMX简介和Standard MBean的相关文章

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

从零开始玩转JMX(一)--简介和Standard MBean JMX超详细解读 参考资料 JMX整理 JMX简介 http://blog.csdn.net/DryKillLogic/article/category/762777 用Apache的commons-modeler来辅助开发JMX

《开源框架那点事儿13》:基于开源框架做应用是未来中小型软件公司的发展趋势

在我的周边朋友身边就发生过这样的事情: 故事1:A君在北京从事Java开发好多年了,萌发了创业的念头,想组建了一个开发团队想大干一场.但是慢慢发现,构建一个有战斗力的团队真不容易.后来技术团队的组建初步有了起色,但是技术路线却非常难成一致意见.折腾来折腾去,把有点上道的技术人员都折腾得跳槽了.费了巨高的成本搞了一个架构师,就是利用SSH框架搭建了一个开发环境,数据量小,业务初期还是不错的,但是当业务快速增长的时候,运行速度就无法满足需要了.是重新来过还是在SSH的基础上继续折腾,非常难以抉择!

第一次做安卓项目使用的开源框架列表

由于以前没有安卓开发经验,虽然Java挺熟悉的,不过到了安卓还是有些不适应,毕竟是另外一套了,想着安卓应该也有许多框架可以帮助我做很多事情了,所以前前后后试了很几个,最终都不怎么理想.比如最一开始用的是 ThinkAndroid ThinkAndroid(一个ThinkAndroid教程地址:http://my.oschina.net/u/1046077/blog/127603)虽然有很多使用的功能,如下,很齐全,而且下面这些功能做一个安卓项目都是需要的 MVC模块 ioc模块使用方法: 数据库

iOS第三方做滤镜最主流的开源框架GPUImage

GPUImage是现在iOS做滤镜最主流的开源框架.作者BradLarson基于openGL对图片处理单元进行封装,提供出GPUImageFilter基类,配合shader,常用滤镜都拿下不是问题.  GPUImage中的有几个概念: ? output,输出源 ? intput,输入源 ? filter,滤镜 所以一个完整的滤镜处理流程是: output+X+input,X就是滤镜组(1+个滤镜).GPUImage为了方便,新版本中提供了GPUImageFilterPipeline 这个东西,方

初识轻量级Java开源框架 --- Spring

初识轻量级Java开源框架 --- Spring 作者:egg 微博:http://weibo.com/xtfggef 出处:http://blog.csdn.net/zhangerqing spring是一个轻量级Java框架,其核心思想就是DI(Dependency Injection,即依赖注入)和IoC(Inversion of Control,即控制反转),因为其开源.低侵入性,现在已经席卷了很大一部分市场,其最大竞争对手乃是JavaEE框架EJB.EJB3.0以前,由于其笨重以及使用

Android开源框架 Android-Universal-Image-Loader

Android开源框架Universal-Image-Loader就像图片加载守护者,为我们提供了丰富的功能特性: (1)多线程加载图像(异步或同步): (2)高度可定制化imageloader配置(线程池.图片下载器.解码器.内存和磁盘缓存.显示图像选项等): (3)每一个显示图像有许多自定义选项(存根图片,缓存开关,解码选项,位图处理和显示等): (4)支持内存和磁盘上的图像缓存(设备的文件系统和SD卡): (5)监听加载过程(包括下载进度): 下来我们详解如何配置使用Universal-I

《开源框架那点事儿25》:对框架模板引擎实现方式的改造实录

点滴悟透设计思想,Tiny模板引擎优化实录! 增加框架设计兴趣小组:http://bbs.tinygroup.org/group-113-1.html Tiny模板引擎的实现方式原来是採用的编译方式,近期发生了一些问题.因此我认为有必要把编译方式调整为解释方式,为此就開始了此次实现活动. 编译方式存在的问题 当时採用编译方式.主要是考虑到编译方式在执行时不必再去遍历语法树.因此就採用了编译方式. 可是在实际应用其中,出现了例如以下问题: 文件路径冲突的问题 因为採用的是编译方式,这个时候就存在在

iOS开发-常用第三方开源框架介绍(你了解的ios只是冰山一角)

iOS开发-常用第三方开源框架介绍(你了解的ios只是冰山一角) 2015-04-05 15:25 2482人阅读 评论(1) 收藏 举报开源框架 图像: 1.图片浏览控件MWPhotoBrowser       实现了一个照片浏览器类似 iOS 自带的相册应用,可显示来自手机的图片或者是网络图片,可自动从网络下载图片并进行缓存.可对图片进行缩放等操作.      下载:https://github.com/mwaterfall/MWPhotoBrowser目前比较活跃的社区仍旧是Github,

《开源框架那些事儿21》:巧借力与借巧力

同样做前端UI,为什么有人花了一点力气,就可以做好?而有的人费尽全力,仍然错误百出?我们可以先看看几个故事. 故事1:巧借力,乌鸦也可以吃核桃 有一个盛产核桃的村子,每年秋末冬初,成群的乌鸦总会来到这里,到果园里捡拾那些被果农们遗落的核桃. 核桃仁虽然美味,但是外壳那么坚硬,乌鸦怎么才能吃到呢?原来乌鸦先把核桃叼起,然后飞到高高的树枝上,再将核桃摔下去,核桃落到坚硬的地面上,被撞破了,于是,乌鸦就得到了那美味的核桃仁. 可是,让核桃从高空坠落,核桃壳破裂的概率很低,很多时候,乌鸦都是望而兴叹.然