• JMX简单入门


    定义

    JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。简答来说就是我们可以通过JMX来监控或管理我们运行中的程序。

    架构

    可以看到主要有3层

    1. 基础层
      主要是各种MBean,被管理的资源,有4种类型
    • standard MBean,标准的MBean,它能管理的资源(包括属性,方法,时间)必须定义在接口中,然后MBean必须实现这个接口。它的命名也必须遵循一定的规范,例如我们的MBean为Hello,则接口必须为HelloMBean。

    • dynamic MBean,必须实现javax.management.DynamicMBean接口,所有的属性,方法都在运行时定义,框架Tomcat,Spring中所使用的都是这种

    • open MBean,实现javax.management.DynamicMBean接口且getMBeanInfo()方法返回的MBeanInfo必须实现javax.management.openmbean.OpenMBeanInfo接口

    • model MBean,也是一种DynamicMBean,管理的资源通过set方法设置

    1. 适配层
      主要组件是MBean服务器,MBean服务器提供MBean的注册使用,它是JMX代理的核心
    2. 接入层
      是JMX架构对外一层,负责使用JMX对外部世界可用。有两种类型的交互。第一种类型是由适配器来实现,它通过HTTP或SNMP一类的协议提供了MBean的可见性。第二种类型是连接器,它将代理的API暴露给其它分布式技术,如Java RMI。

    实例

    JDK的jconsole工具访问

    以下代码都以标准MBean作为例子。
    定义一种MBean接口

    public interface HelloMBean {
    
      String getName();
    
      void setName(String name);
    
      String getAge();
    
      void setAge(String age);
    
      void helloWorld();
    
      void helloWorld(String str);
    
      void getTelephone();
    }
    

    定义实现类,类名必须和接口的前缀一致

    public class Hello implements HelloMBean {
    
      private String name;
    
      private String age;
    
      public void getTelephone() {
        System.out.println("get Telephone");
      }
    
      public void helloWorld() {
        System.out.println("hello world");
      }
    
      public void helloWorld(String str) {
        System.out.println("helloWorld:" + str);
      }
    
      public String getName() {
        System.out.println("get name 123");
        return name;
      }
    
      public void setName(String name) {
        System.out.println("set name 123");
        this.name = name;
      }
    
      public String getAge() {
        System.out.println("get age 123");
        return age;
      }
    
      public void setAge(String age) {
        System.out.println("set age 123");
        this.age = age;
      }
    }
    

    定义适配层

    public class HelloAgent {
    
      public static void main(String[] args) throws JMException, Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        //规范 域名:name=MBean名称
        ObjectName helloName = new ObjectName("jmxBean:name=hello");
        //注册MBean
        server.registerMBean(new Hello(), helloName);
        Thread.sleep(60 * 60 * 1000);
      }
    }
    

    使用jconsole连接

    连接之后我们可以看到我们自己定义的MBean

    我们可以对属性进行获取或操作,也可以调用方法

    程序控制台输出如下:

    通过JMX提供的工具页访问

    这个时候需要第三方jar包jdmk

    <dependency>
       <groupId>com.sun.jdmk</groupId>
       <artifactId>jmxtools</artifactId>
       <version>1.2.1</version>
    </dependency>
    

    但其实maven中央库这个jar已经不可访问了,需要的话可以从这下载jdmk
    下载之后我们可以通过maven命令将jar安装到本地仓库

    mvn install:install-file -DgroupId=com.sun.jdmk -DartifactId=jmxtools -Dversion=1.2.1 -Dpackaging=jar -Dfile=‪D:javalogjdmkjmxtools-1.2.1.jar
    

    接口和实现类还是使用上面的,改动适配层

    public class HelloAgent {
    
      public static void main(String[] args) throws JMException, Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        //规范 域名:name=MBean名称
        ObjectName helloName = new ObjectName("jmxBean:name=hello");
        //注册MBean
        server.registerMBean(new Hello(), helloName);
        ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082");
        //端口号默认8082
        HtmlAdaptorServer adapter = new HtmlAdaptorServer();
        server.registerMBean(adapter, adapterName);
        adapter.start();
      }
    }
    

    浏览器访问http://localhost:8082/

    可以和jconsole页面上进行相同的操作

    通过客户端程序远程访问

    public class HelloAgent {
    
      public static void main(String[] args) throws JMException, NullPointerException {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        ObjectName helloName = new ObjectName("jmxBean:name=hello");
        //create mbean and register mbean
        server.registerMBean(new Hello(), helloName);
        try {
          //这个步骤很重要,注册一个端口,绑定url后用于客户端通过rmi方式连接JMXConnectorServer
          LocateRegistry.createRegistry(9999);
          //URL路径的结尾可以随意指定,但如果需要用Jconsole来进行连接,则必须使用jmxrmi
          JMXServiceURL url = new JMXServiceURL
              ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
          JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
          System.out.println("begin rmi start");
          jcs.start();
          System.out.println("rmi start");
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    

    使用jconsole连接

    使用客户端程序连接

    public class Client {
    
      public static void main(String[] args) throws IOException, Exception, NullPointerException {
        JMXServiceURL url = new JMXServiceURL
            ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
    
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
        //ObjectName的名称与前面注册时候的保持一致
        ObjectName mbeanName = new ObjectName("jmxBean:name=hello");
    
        System.out.println("Domains ......");
        String[] domains = mbsc.getDomains();
    
        for (int i = 0; i < domains.length; i++) {
          System.out.println("doumain[" + i + "]=" + domains[i]);
        }
    
        System.out.println("MBean count = " + mbsc.getMBeanCount());
        //设置指定Mbean的特定属性值
        //这里的setAttribute、getAttribute操作只能针对bean的属性
        //例如对getName或者setName进行操作,只能使用Name,需要去除方法的前缀
        mbsc.setAttribute(mbeanName, new Attribute("Name", "杭州"));
        mbsc.setAttribute(mbeanName, new Attribute("Age", "1990"));
        String age = (String) mbsc.getAttribute(mbeanName, "Age");
        String name = (String) mbsc.getAttribute(mbeanName, "Name");
        System.out.println("age=" + age + ";name=" + name);
    
        HelloMBean proxy = MBeanServerInvocationHandler.
            newProxyInstance(mbsc, mbeanName, HelloMBean.class, false);
        proxy.helloWorld();
        proxy.helloWorld("migu");
        proxy.getTelephone();
        //invoke调用bean的方法,只针对非设置属性的方法
        //例如invoke不能对getName方法进行调用
        mbsc.invoke(mbeanName, "getTelephone", null, null);
        mbsc.invoke(mbeanName, "helloWorld",
            new String[]{"I'll connect to JMX Server via client2"}, new String[]{"java.lang.String"});
        mbsc.invoke(mbeanName, "helloWorld", null, null);
      }
    }
    

    通过程序可以达到和jconsole页面同样的效果

    MBean之间通信

    通过发送和接受Notification来实现通信,JMX的通信包括4部分

    • Notification,这个相当于一个信息包,封装了需要传递的信息
    • NotificationBroadcaster,这个相当于一个广播器,把消息广播出。
    • NotificationListener,这是一个监听器,用于监听广播出来的通知信息。
    • NotificationFiliter,这个一个过滤器,过滤掉不需要的通知。

    定义消息广播器,也是一个MBean

    public interface MyNotificationBroadcasterMBean {
      void hi();
    }
    
    public class MyNotificationBroadcaster extends NotificationBroadcasterSupport implements
        MyNotificationBroadcasterMBean {
    
      private int seq = 0;
    
      public void hi() {
        //创建一个信息包 通知名称;谁发起的通知;序列号;发起通知时间;发送的消息
        Notification notify = new Notification("hi", this, ++seq, System.currentTimeMillis(),
            "How are you?");
        sendNotification(notify);
      }
    
    }
    

    定义消息监听器

    public class HelloListener implements NotificationListener {
    
      public void handleNotification(Notification notification, Object handback) {
        System.out.println(notification.getMessage());
      }
    
    }
    

    适配层

    public class HelloAgent {
    
      public static void main(String[] args) throws Exception {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        MyNotificationBroadcaster notificationBroadcaster = new MyNotificationBroadcaster();
        server.registerMBean(notificationBroadcaster,
            new ObjectName("MyNotificationBroadcaster:name=myNotificationBroadcaster"));
        notificationBroadcaster.addNotificationListener(new HelloListener(), null, null);
        Thread.sleep(500000);
      }
    }
    

    通过jconsole来调用消息广播器的hi()方法,可以看到控制台的输出

    参考

    JMX超详细解读

  • 相关阅读:
    JVM运行时数据区--堆
    ES检索服务搜索结果高亮
    SpringBoot 设置编码UTF-8
    response.setContentType()的作用及参数
    将 vue.js 获取的 html 文本转化为纯文本
    SpringBoot读取properties文件配置项
    关于Java的编译执行与解释执行
    Java沙箱安全机制介绍【转载】
    JVM运行时数据区--本地方法栈
    JVM--先说本地方法接口
  • 原文地址:https://www.cnblogs.com/strongmore/p/14053569.html
Copyright © 2020-2023  润新知