精通JVM监控,不知道JMX?

JMX是JavaManagementExtensions,用于管理和监控Java程序,特别是JVM的内存、CPU等。它包括标准、规范和框架,允许开发者定制管理功能。MBean是JMX的核心,有StandardMBean、DynamicMBean等类型。MBeanServer管理所有MBean,而适配器和连接器如RMI则使功能可被外部访问。文章还介绍了如何定义和使用MBean,以及通过JConsole和RMI进行监控。

JMX简介

JMX 全称为 Java Management Extensions,翻译过来就是 Java 管理扩展,用来管理和监测 Java 程序。最常用到的就是对于 JVM 的监测和管理,比如 JVM 内存、CPU 使用率、线程数、垃圾收集情况等等。另外,还可以用作日志级别的动态修改,比如 log4j 就支持 JMX 方式动态修改线上服务的日志级别。最主要的还是被用来做各种监控工具,比如Spring Boot Actuator、JConsole、VisualVM 等。

JMX 既是 Java 管理系统的一个标准,一个规范,也是一个接口,一个框架。有标准、有规范是为了让开发者可以定制开发自己的扩展功能,而且作为一个框架来讲,JDK 已经帮我们实现了常用的功能,尤其是对 JVM 的监控和管理。

上图是 JMX 架构的简单示意图,简单理解就是管理系统通过 JMX 可以管理各种资源。管理系统可以理解为管理客户端,比如上面说的 JConsole、VisualVM ,还有 Metrics 这个非常知名的 Java 监控工具包,或者你自己通过 JMX 接口实现的客户端等。 

这个架构图是把上面的架构示意图展开了,下面从底向上简单介绍一下。

MBean

JMX 是通过各种 MBean(Managed Bean) 传递消息的,MBean 其实就是我们经常说的 Java Bean,只不过由于它比较特殊,所以称之为 MBean。既然是个 Bean,里面就是一些属性和方法,外界就可以获取被管理的资源的状态和操纵MBean的行为。JMX 中共有四种类型的 MBean,分别是 Standard MBean, Dynamic MBean, Open MBean, Model MBean。JDK 提供的 MBean 主要在 java.lang.managementjavax.management包里面。可以进去看一看,进去就能看到好多似曾相识的身影,比如 Memory 相关的、Thread 相关的,这不就是我们在 VisualVM 上看到的内容吗,没错,数据就是从这里来的。

Standard MBean 就是普通的 Java Bean 没有区别,它也是 JMX 中最简单、使用最多的一种。主要在java.lang.management包里面。

Dynamic MBean 其实是一种妥协的产物,由于已经存在一些这种 MBean,而将其改造成标准 MBean 比较费力而且不切实际,所以就有了动态 MBean。Dynamic MBean 的接口在 javax.management.DynamicMBean这里,里面定义一些接口方法,比如动态获取属性、设置属性等。

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 都是使用它。

实现并使用一个 MBean

虽然 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 查看

Console 是 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 看到的就是这些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值