JMX学习

news/2025/3/19 21:18:22/

1. 絮絮叨叨

1.1 Java程序包含哪些线程?

  • 使用Java进行多线程编程的人,多少可能都知道执行main()方法的是一个名为main的线程

  • 通过new Thread新建线程,若不指定线程名,默认线程名为Thread-xx。其中xx是从0开始的编号

    public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> System.out.println("I'm " + Thread.currentThread().getName())).start();}
    }
    
  • 为何默认线程名为Thread-xx,通过Thread类的对应构造函数就可以知道

    public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    
  • 其实,早在很久之前,自己就有过一个疑问:一个Java程序除了我们所知的main线程、自定义线程,是否还包含其他线程?

  • 据我目前掌握的知识,通过jstack命令可以实现

  • 在学习并发编程时,发现有一个非常好用的ThreadMXBean接口,就可以帮助我们了解Java程序究竟包含哪些线程

    public static void main(String[] args) {ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);for (ThreadInfo threadInfo : threadInfos) {System.out.println("[" + threadInfo.getThreadId() + "]" + " " + threadInfo.getThreadName());}
    }
    
  • 获取的线程信息如下,线程10和5,根据自己的经验,应该是idea在运行Java程序时自己添加的线程,以实现监听、响应程序中断

1.2 关于ThreadMXBean接口

  • ThreadMXBean是接口,通过ManagementFactory获取到的肯定是ThreadMXBean接口的实现类的一个实例对象

  • 这里使用了Java的多态,将接口的实现类的对象赋值给接口引用

  • ThreadImpl就是ThreadMXBean接口一个实现类

    public static ThreadMXBean getThreadMXBean() {return ManagementFactoryHelper.getThreadMXBean();
    }public static synchronized ThreadMXBean getThreadMXBean() {if (threadMBean == null) {threadMBean = new ThreadImpl(jvm);}return threadMBean;
    }
    

其他使用场景

  • 通过ThreadMXBean除了可以获取Java程序中的线程信息,还支持很多其他有用的操作
    • 获取程序中死锁的线程ID:findDeadlockedThreads()findMonitorDeadlockedThreads() ( Java ThreadMXBean & 死锁检测 )
    • 获取线程数:当前活动线程数getThreadCount()、当前活动的守护线程数getDaemonThreadCount()、活动线程数峰值getPeakThreadCount()
    • 获取cpu时间:getThreadCpuTime(long id),获取用户态cpu时间:getThreadUserTime(long id)

2. JMX概述

  • 通过ThreadMXBean获取Java程序中的线程信息时,资料中有这样的描述

    下面使用JMX来查看一个普通的Java程序包含哪些线程

  • 看完示例代码后,感觉这就是JMX?好像和自己了解的不太一样?

  • 目前,很多大数据组件都可以通过JMX获取节点或者集群的运行情况,这些不同维度的数据一般被称作metric

  • 以Presto为例

    • presto的指标有:集群或节点内存信息,正在运行的查询数、查询失败率,gc次数和cpu时间等
    • 基于jmx这个catalog,执行SQL获取对应metric的数据
    • 将metric的值上报到kafka,借助Druid + Granfna实现对集群或节点的实时监控
    • 一旦发现某些metric值异常,可以进行监控告警
  • 因此,自己对jmx的认知就是:一个可以动态获取程序运行状态的工具,对监控程序的运行非常有用

JMX的定义

  • JMX是Java Management Extensions 的缩写,是Java的一种管理扩展工具
  • 官方定义: JMX是一套标准的代理和服务,用户可以在任何Java应用程序中使用这些代理和服务实现管理
  • 可以通过JDK工具JConsole、网页、客户端与JMX服务器进行交互,从而管理或获取程序的状态
  • 中间件软件WebLogic的管理页面、Tomcat、Jboss等都是基于JMX开发的

JMX架构图

  • JMX的底层:又称基础层,是被管理的对象MBean。
    • 主要有三种:标准MBean、动态MBean和MXBean,本文主要基于标准MBean实现JMX
  • 代理层:MBeanServer,对MBean进行注册和管理。
  • 远程管理层:又称接入层,可以通过http、RMI、SNMP等方式实现对MBeanServer的远程访问

一些说明

  • 如果只把JMX看做是动态获取程序运行状态的工具,是比较狭隘的
  • 因为,通过JMX不仅可以获取程序运行状态,还可以向程序传递参数从而影响程序的运行
  • MBean中,广义的get方法,提供获取程序运行状态的参数;广义的set方法,可以向程序传递参数

3. 实战

  • 通过JMX管理Java程序,需要以下三步
    • 定义MBean接口(实现代理的基础),实现MBean:
      • MBean接口名必须以MBean为后缀,以MBean实现类为前缀
      • 例如,一个MBean接口为HelloMBean,则其实现类为Hello
    • 向MBeanServer注册MBean
    • 以某种方式进行代理访问,从而实现与MBean的交互

3.1 基于JConsole的JMX示例

  • 创建管理学生的MBean接口:

    • 两个属性nam和age,需要针对属性添加getter/setter方法,以保证可读/可写
    • 其中,name可读可写,而age只读
    • 注意: 获取、设置值的方法不能随意定义为getXXX或者setXXX,否则XXX将被解读成属性。实际上,它可能并不是属性字段
    public interface StudentMBean {String getName();void setName(String name);int getAge();void printStudentInfo();String studentInfo();
    }
  • 实现MBean接口

    class Student implements StudentMBean {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String getName() {return name;}@Overridepublic void setName(String name) {this.name = name;}@Overridepublic int getAge() {return age;}@Overridepublic void printStudentInfo() {System.out.println("Student: " + name + ", age: " + age);}@Overridepublic String studentInfo() {return "Student: " + name + ", age: " + age;}
    }
    
  • 向MBeanServer注册MBean

    public class JConsoleAgent {public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException,InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException {// 创建MBeanServerMBeanServer server = ManagementFactory.getPlatformMBeanServer();// 创建ObjectName以唯一标识MBean,其中studentMBean为域名,boy为MBean的名称,可以自由定义ObjectName student = new ObjectName("studentMBean:name=boy");// 将student注册到MBeanServer中,注册时实现了ObjectName与MBean的绑定server.registerMBean(new Student("jack", 24), student);// 程序休眠一段时间,方便观察通过JConsole体验JMXTimeUnit.MINUTES.sleep(30);}
    }
    
  • 在命令行中输入jconsole以启动JConsole工具,选择对应的代理创建连接

  • 最终,studentBean的信息如下:

  • name属性为可读可写,可以直接修改name(通过刷新按钮,实现值的修改)

  • printStudentInfo()是一个无参、void方法,直接点击方法名即可运行该方法

  • printStudentInfo方法有输出,最终会在JConsoleAgent的运行界面打印输出信息

3.2 基于网页的JMX示例

  • 添加HtmlAdaptorServer的maven依赖

    <!-- https://mvnrepository.com/artifact/com.sun.jdmk/jmxtools -->
    <dependency><groupId>com.sun.jdmk</groupId><artifactId>jmxtools</artifactId><version>1.2.1</version>
    </dependency>
    
  • 保持MBean不变,创建HttpAdapterAgent,通过对JConsoleAgent进行一些改造,就可以通过网页与JMX进行交互

    public class HttpAdapterAgent {public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException,InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException {// 创建MBeanServerMBeanServer server = ManagementFactory.getPlatformMBeanServer();// 创建ObjectName以唯一标识MBean,其中studentMBean为域名// boy为MBean的名称,可以自由定义ObjectName student = new ObjectName("studentMBean:name=boy");// 将student注册到MBeanServer中,注册时实现了ObjectName与MBean的绑定server.registerMBean(new Student("jack", 24), student);// 创建并注册HtmlAdaptorServer,从而可以通过网页管理MBeanHtmlAdaptorServer adaptorServer = new HtmlAdaptorServer();ObjectName adapter = new ObjectName("httpAdapter:name=web");server.registerMBean(adaptorServer, adapter);// 启动HtmlAdaptorServeradaptorServer.start();}
    }
    
  • 访问本地的8082端口:http://localhost:8082/,将存在如下网页

  • 选择进入studentMBean域、name为boy的MBean,信息展示更加清晰】

3.3 通过客户端进行远程访问

定义支持远程访问的MBeanServer

  • 修改Agent使其支持远程访问

    public class RemoteAgent {public static void main(String[] args) throws MalformedObjectNameException, NotCompliantMBeanException,InstanceAlreadyExistsException, MBeanRegistrationException, InterruptedException, IOException {// 创建MBeanServerMBeanServer server = ManagementFactory.getPlatformMBeanServer();ObjectName student = new ObjectName("studentMBean:name=girl");server.registerMBean(new Student("lucy", 24), student);// 为MBeanServer注册端口号和urlLocateRegistry.createRegistry(8888);// 若需要支持JConsole链接,url的结尾必须为jmxrmiJMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi");// 创建支持远程连接的服务器JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);// 启动服务connectorServer.start();}
    }
    
  • 此时,尚未定义客户端,可以先通过JConsole进行访问

  • studentMBean域、name为girl的MBean信息如下

定义客户端

  • 通过MBeanServer的url实现对指定MBean的管理
    • 可以直接通过MBeanServerConnection访问MBean中的属性和方法,也可以通过代理实现访问
    • 注意: 属性值定义时首字母虽然为小写,但通过JMX解析后实际是首字母大写
    public class Client {public static void main(String[] args) throws IOException, MalformedObjectNameException, AttributeNotFoundException, MBeanException, ReflectionException, InstanceNotFoundException, InvalidAttributeValueException {// 创建与server的连接JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:8888/jmxrmi");JMXConnector jmxConnector = JMXConnectorFactory.connect(url, null);MBeanServerConnection serverConnection = jmxConnector.getMBeanServerConnection();// 定义想要访问的MBean,与MBeanServer中注册的一致ObjectName student = new ObjectName("studentMBean:name=girl");// 获取MBeanServer所有domainString[] domains = serverConnection.getDomains();System.out.println("MBeanServer存在如下域名:");for (String domain: domains) {System.out.println(domain);}System.out.println("=================================");// 直接修改或访问属性值,属性必须大写serverConnection.setAttribute(student, new Attribute("Name", "grace"));System.out.println("Student: " + serverConnection.getAttribute(student, "Name"));// 直接调用MBean中的方法String info = (String) serverConnection.invoke(student, "studentInfo", null, null);System.out.println(info);// 创建代理实现访问StudentMBean proxy = MBeanServerInvocationHandler.newProxyInstance(serverConnection, student, StudentMBean.class, false);System.out.println("age: " + proxy.getAge());}
    }
    
  • 访问结果如下

3.4 JVM参数 + JCOnsol,实现JMX远程访问

  • 2.1中基于JConsole的JMX示例,只能通过本地进程的方式进行访问

  • 如果想要以最小的代价使其支持远程访问,可以在程序运行时添加以下jvm参数(idea的run Configurations)

    -Dcom.sun.management.jmxremote
    -Dcom.sun.management.jmxremote.port=8880
    -Dcom.sun.management.jmxremote.authenticate=false
    -Dcom.sun.management.jmxremote.ssl=false
    -Djava.rmi.server.hostname=localhost
    
  • 然后重新启动JConsoleAgent,通过localhost:8880便可实现远程访问

  • 注意: 任意一个Java程序,都包含默认的MBean,如下图所示

  • 通过java命令启动程序的命令如下,注意:请使用类的完全限定名

    java -Dcom.sun.management.jmxremote  -Dcom.sun.management.jmxremote.port=8880 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=localhost vivo.internet.study.jmx.JConsoleAgent 
    
  • 当然,同样可以通过Client程序实现与JMX的交互:

    service:jmx:rmi:///jndi/rmi://localhost:8880/jmxrmi
    

4. 总结

  • 通过工作所接触的大数据组件,自己对JMX的理解非常表面:就是一个可以动态获取程序运行状态的工具,从而实现对Java程序的监控

  • 通过这次的学习,发现JMX的强大远超想象:

    • 可以通过MBean获取程序的状态
    • 可以修改MBean中属性,从而实现动态向程序传参
    • 可以调用MBean中的方法,实现某些操作
    • 甚至可以在多个MBean之间进行通信,参考博客:jmx学习
  • JMX的架构:基础层、代理层、接入层,不同的接入方式:JConsole、网页、客户端

  • JMX的实现示例

    • 实现JMX的三大步骤:MBean的定义与实现、MBean的注册、以某种方式接入JMX
    • 基于JConsole(本地访问)、网页、远程访问(客户端和JConsole),三种不同接入方式的具体实现
    • 如果通过JVM参数实现并简化JMX的远程访问实现
  • 一些注意事项:

    • 通过ObjectName实现MBean实例的唯一标识,包含域名和MBean的name
    • MBean的属性一般首字母小写,客户端远程访问时需要首字母大写
    • 远程访问时,客户端想访问的Mbean必须与MBeamServer中注册的MBean一致

参考文档:

  • jmx学习
  • 使用jvisualvm通过JMX的方式远程监控JVM的运行情况

http://www.ppmy.cn/news/707395.html

相关文章

Android时区问题

最近做外销机&#xff0c;在实现设置时区的功能中&#xff0c;遇到了一些问题&#xff0c;特此记录。 1&#xff0c;如何获取到时区列表&#xff1f;如何设置时区&#xff1f; 2&#xff0c;默认时区怎么设置&#xff1f; 3&#xff0c;自动时区是怎么回事&#xff1f; 一&…

JMX的配置

tomcat JMX配置 先修改Tomcat的启动脚本&#xff0c;windows下为bin/catalina.bat&#xff08;linux下为catalina.sh&#xff09;&#xff0c;添加以下内容 不启用密码验证的配置: Window系统: set JAVA_OPTS%JAVA_OPTS% -Dcom.sun.management.jmxremote 相关 JMX 代理侦…

MM--MIGO的屏幕格式由来学习

migo的屏幕格式的由来migo的屏幕格式是根据用户选择的操作&#xff0c;由操作决定参考单据&#xff08;操作和参考单据有个固定关系&#xff0c;在次关系基础上用户可以配置哪些TCODE可以使用哪些操作&#xff0c;操作参照哪些文档&#xff09;&#xff0c;在根据用输入的操作和…

参数非法异常:java.lang.IllegalArgumentException: class com.xxx.SignDetailRQ declares multiple JSON fields

一大早发现了一个异常&#xff1a; java.lang.IllegalArgumentException: class com.xxx.request.SignDetailRQ declares multiple JSON fields named projectId at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.…

【Gem5】有关gem5模拟器的资料导航

网上有关gem5模拟器的资料、博客良莠不齐&#xff0c;这里记录一些总结的很好的博客与自己的学习探索。 一、gem5模拟器使用入门 官方的教程&#xff1a; learning_gem5&#xff1a;包括gem5简介、修改扩展gem5的示例、Ruby相关的缓存一致性等。gem5 Documentation&#xff1…

JMX使用入门

简述 本文是关于JMX&#xff08;Java Management Extensions&#xff09;的使用文档。 关于JMX 所谓JMX&#xff0c;是Java Management Extensions(Java管理扩展)的缩写&#xff0c;是一个为应用程序植入管理功能的框架。JMX有以下用途&#xff1a; 监控应用程序的运行状态和…

gmii_to_rgmii的使用心得

以zynq7工程为基础&#xff1b; 在PS内使能“Ethernet”选用emio&#xff0c;再添加IP核“gmii_to_rgmii”&#xff0c;这部分的搭建工程的文章很多&#xff0c;故不再叙述。 IP核设置很简单&#xff0c;如图。 这里有一点需要说明的是&#xff1a;如果调测是发现发送端TX不正…

gin初体验

一、配置镜像 go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct二、创建go项目 三、安装依赖 go get -u github.com/gin-gonic/gin四、新建入口 package mainimport ("github.com/gin-gonic/gin""net/http" )func Hello(c *gin.Con…