相关文章目录:html
开源框架是如何经过JMX来作监控的(一) - JMX简介和Standard MBeanjava
开源框架是如何经过JMX来作监控的(二) - Druid链接池的监控数据库
相信不少作Java开发的同窗都使用过JDK自带的 jconsole 或者 jvisualvm 监控过JVM的运行状况,但不知道有没有留意过它们会有一个MBean的功能/标签,经过MBean能够看到在JVM中运行的组件的一些属性和操做设计模式
例如,能够看到Tomcat 8080端口Connector的请求链接池信息,Druid数据库链接池的activeCount链接数以及链接池配置信息,这些开源框架或中间件都是经过JMX的方式将本身的一些管理和监控信息暴露给咱们api
JMX的全称为Java Management Extensions. 顾名思义,是管理Java的一种扩展。这种机制能够方便的管理正在运行中的Java程序。经常使用于管理线程,内存,日志Level,服务重启,系统环境等。服务器
下面简单介绍一下JMX和其最简单的Standard MBean,以及经常使用实例,以后的文章中会分析Druid数据库链接池等开源框架是如何经过JMX作监控的,没准在咱们的程序中也能够用到。架构
如下是本文的目录大纲:框架
1、JMX架构及基本概念dom
如有不正之处请多多谅解,欢迎批评指正、互相讨论。
请尊重做者劳动成果,转载请标明原文连接:
http://www.cnblogs.com/trust-freedom/p/6842332.html
从上面的架构图能够看到JMX主要分三层,分别是:
一、设备层(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。
二、代理层(Agent Level)
Agent层 用来管理相应的资源,而且为远端用户提供访问的接口。Agent层构建在设备层之上,而且使用并管理设备层内部描述的组件。Agent层主要定义了各类服务以及通讯模型。该层的核心是 MBeanServer,全部的MBean都要向它注册,才能被管理。注册在MBeanServer上的MBean并不直接和远程应用程序进行通讯,他们经过 协议适配器(Adapter) 和 链接器(Connector) 进行通讯。一般Agent由一个MBeanServer和多个系统服务组成。JMX Agent并不关心它所管理的资源是什么。
三、分布服务层(Distributed Service Level)
分布服务层关心Agent如何被远端用户访问的细节。它定义了一系列用来访问Agent的接口和组件,包括Adapter和Connector的描述。
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); } }
继续上面的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,则大概流程以下: 一、将jmx connect server内部的server对象的rmi stub export到本地的一个随机端口(也能够本身指定),接收外部链接 二、经过jndi api将该stub绑定在rmi://localhost:6000/jmxrmi这个地址上,这须要在本地的1099端口上运行着一个rmiregistry,若是不存在则会抛出异常 |
若是在客户端,咱们经过该URL建立一个connector,则大概流程以下: 一、访问1099端口,经过jndi api到rmi://localhost:1099/jmxrmi这个地址取回stub 二、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的例子,不少开源框架(如一些数据库链接池DBCP二、Druid)都实现了JMX来达到对其自身的监控,虽然这不是惟一的方法,也能够经过其它方式提供监控查询接口,但因为JMX是sun提出的通用标准,故你们纷纷响应实现。因此当咱们使用这些开源框架并但愿对其运行情况作一些管理监控时,能够采用JMX的方式获取其暴露出的MBean相关属性和方法。以后会分析一些Druid是如何经过JMX的方式作管理监控的。
打开JDK自带的VisualVM,因为本例是本地localhost的JMX server,那么在左侧栏“本地”上右键,建立JMX链接
点击后只需填写本地的RMIRegistry注册的端口1099便可,固然也能够填写完整的URL:service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
链接成功后,就能够看到HelloMBean以及其提供的属性和操做,还能够调用printHello()方法
参考资料: