若是你以前没接触过,必定会出现疑问三连击,"这是个什么玩意儿?干吗的?有啥用?"。java
虽然可能不知道它,可是或多或少你确定都接触过。web
好比你若是用 Spring Boot,那你用过 Spring Boot Actuator 吧,它就用到了 JMX 。好比你用过 JConsole 或者 VisualVM 吧,它们也用到了 JMX。浏览器
下图是 JConsole 和 VisualVM 的界面。架构
它们是如何用到了 JMX 呢,下面一步一步说。框架
JMX 全称为 Java Management Extensions,翻译过来就是 Java 管理扩展,用来管理和监测 Java 程序。最经常使用到的就是对于 JVM 的监测和管理,好比 JVM 内存、CPU 使用率、线程数、垃圾收集状况等等。另外,还能够用做日志级别的动态修改,好比 log4j 就支持 JMX 方式动态修改线上服务的日志级别。最主要的仍是被用来作各类监控工具,好比文章开头提到的 Spring Boot Actuator、JConsole、VisualVM 等。dom
JMX 既是 Java 管理系统的一个标准,一个规范,也是一个接口,一个框架。有标准、有规范是为了让开发者能够定制开发本身的扩展功能,并且做为一个框架来说,JDK 已经帮咱们实现了经常使用的功能,尤为是对 JVM 的监控和管理。ide
上图是 JMX 架构的简单示意图,简单理解就是管理系统经过 JMX 能够管理各类资源。工具
管理系统能够理解为管理客户端,好比上面说的 JConsole、VisualVM ,还有 Metrics 这个很是知名的 Java 监控工具包,或者你本身经过 JMX 接口实现的客户端等。
各类资源好比系统配置、JVM 指标等,或者你本身的项目中特定的资源等。url
重点来了,JMXspa
这个架构图是把上面的架构示意图展开了,下面从底向上简单介绍一下。
JMX 是经过各类 MBean(Managed Bean) 传递消息的,MBean 其实就是咱们常常说的 Java Bean,只不过因为它比较特殊,因此称之为 MBean。既然是个 Bean,里面就是一些属性和方法,外界就能够获取被管理的资源的状态和操纵MBean的行为。JMX 中共有四种类型的 MBean,分别是 Standard MBean, Dynamic MBean, Open MBean, Model MBean。JDK 提供的 MBean 主要在 java.lang.management
和 javax.management
包里面。能够进去看一看,进去就能看到好多似曾相识的身影,好比 Memory 相关的、Thread 相关的,这不就是咱们在 VisualVM 上看到的内容吗,没错,数据就是从这里来的。
说实话,并不用太关心这几种 MBean 的区别,但仍是简单介绍下。
Standard MBean 就是普通的 Java Bean 没有区别,它也是 JMX 中最简单、使用最多的一种。主要在java.lang.management
包里面。
Dynamic MBean 实际上是一种妥协的产物,因为已经存在一些这种 MBean,而将其改形成标准 MBean 比较费力并且不切实际,因此就有了动态 MBean。Dynamic MBean 的接口在 javax.management.DynamicMBean
这里,里面定义一些接口方法,好比动态获取属性、设置属性等。
另外还有两类 MBean:Open MBean 和 Model MBean,实际上它们也都是动态 MBean。
Open MBean 与其它动态 MBean 的惟一区别在于,前者对其公开接口的参数和返回值有所限制 —— 只能是基本类型或者 javax.management.openmbean
包内的 ArrayType、CompositeType、TarbularType 等类型。这主要是考虑到管理系统的分布,极可能远端管理系统甚至 MBServer 层都不具备 MBean 接口中特殊的类。
MBeanServer
MBeanServer 是负责管理 MBean 的,通常一个 JVM 只有一个 MBeanServer,全部的 MBean 都要注册到 MBeanServer 上,并经过 MBeanServer 对外提供服务。通常用 ManagementFactory.getPlatformMBeanServer()
方法获取当前 JVM 内的 MBeanServer。
适配器和链接器
写好的 MBean 注册到 MBeanServer 上以后,功能已经具有了。适配器和链接器就是将这些功能开放出来的方式。
好比 HTTP协议适配器,就是将功能以 HTTP 协议开放出去,这样咱们就能够在浏览器使用了。可是 JDK 只是提供了适配器的实现标准,并无具体的实现,比较经常使用的是 HtmlAdaptorServer
,须要 jmxtools.jar 包的支持。
链接器是各类客户端最经常使用的,JDK 提供的默认链接器是 RMI 链接器,JConsole、VisualVM 都是使用它。
虽然 Java 提供了实现 MBean 的标准和规则,但平时咱们几乎不须要开发 MBean。绝大多数的开发者接触到的也仅仅是使用 JDK 或者第三方定义好的 MBean,即使是第三方有实现 MBean,也是很是少的。咱们知道的有 Tomcat 和 Spring Boot Actuator。
定义 MBean 接口 和实体类
// 接口 public interface UserMBean { String getName(); String getPassword(); String getPhone(); void say(); }
public class User implements UserMBean { @Override public String getName() { return "风筝"; } @Override public String getPassword() { return "密码不可见"; } @Override public String getPhone() { return "18900000000"; } @Override public void say() { System.out.println("Hello JMX"); } }
实体类须要继承 MBean 接口类,而接口类的命名规则是 「实体类名+MBean」,这是固定的规则。
将定义好的 MBean 注册到 MBeanServer
public static void main(String[] args) throws Exception { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName userName = new ObjectName("FengZheng:type=customer,name=customerUserBean"); server.registerMBean(new User(), userName); try { //这个步骤很重要,注册一个端口,绑定url后用于客户端经过rmi方式链接JMXConnectorServer LocateRegistry.createRegistry(8999); //URL路径的结尾能够随意指定,但若是须要用Jconsole来进行链接,则必须使用jmxrmi JMXServiceURL url = new JMXServiceURL ("service:jmx:rmi:///jndi/rmi://localhost:8999/jmxrmi"); JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server); System.out.println("begin rmi start"); jcs.start(); System.out.println("rmi start"); } catch (RemoteException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } Thread.sleep(60 * 60 * 1000); }
ManagementFactory.getPlatformMBeanServer()
获取当前 JVM 的 MBeanServer,ObjectName 是 MBean 的惟一标示,一个 MBeanServer 不能有重复。完整的格式「自定义命名空间:type=自定义类型,name=自定义名称」。固然你能够只声明 type ,不声明 name。
使用 JConsole 查看
JConsole 是 JDK 自带的工具,在${JAVA_HOME}的 bin 目录下,启动便可。启动后在本地进程找到上一步启动的 main 方法所在的进程。
在 JConsole 上方有内存、线程、类等选项卡,点击最后一个 MBean,经过这个选项卡能够看到当前 JVM 全部已定义的 MBean。能够看到系统定义的 MBean ,以及刚刚咱们定义的这个 MBean。
点击属性能够在右侧查看属性值,而且在操做菜单项下,能够看到咱们定义的方法,而且能够调用。
使用 RMI 方式链接
RMI 通常是用来链接远程服务的,固然本地进程也能够。这也是实现链接远程服务客户端的第一步。咱们在注册 MBean 的时候,有没有注意到注册完成后,还有一大段代码,那段代码就是用来开启 RMI 链接的,开启 8999 端口做为 RMI 访问端口,而后客户端就能够用固定的链接串链接了。
链接串的格式 service:jmx:rmi:///jndi/rmi://host:port/jmxrmi
public class Client { public static void main(String[] args) throws IOException, Exception, NullPointerException { String jmxUrl = "service:jmx:rmi:///jndi/rmi://localhost:8999/jmxrmi"; monitor(jmxUrl); } public static void monitor(String url) throws Exception{ JMXServiceURL jmxServiceURL = new JMXServiceURL (url); JMXConnector jmxc = JMXConnectorFactory.connect(jmxServiceURL, null); MBeanServerConnection msc = jmxc.getMBeanServerConnection(); String[] domains = msc.getDomains(); for (String domain : domains) { System.out.println(domain); } } }
首先根据链接串得到一个 JMXServiceURL
对象,以后经过JMXConnectorFactory.connect
方法获取 JMXConnector
,再经过 getMBeanServerConnection
拿到 MBeanServerConnection
。以后就能够获取全部的 MBean 了。其中 getDomains
是获取全部命名空间的方法,也就是咱们上面定义的 "FengZheng",以及 JMImplementation、java.lang 等,咱们在 JConsole 看到的就是这些。
哎呀,好累,不想写了,原本还想介绍系统定义的 JVM 相关的 MBean 以及具体的操做 MBean 的方式,还有我写这个的时候本身写的一个 web 版的客户端,功能和 JConsole 、VisualVM 基本相同,也支持本地和远程链接。下一篇一块介绍吧,先放两张截图。
支持远程和本地链接
查看全部的 MBean ,以及 MBean 的属性,而且能够调用方法。
动态查看 CPU、堆、metasapce(Java1.8)、类、线程等
更多干货请关注公众号:古时的风筝