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:\java\blog\jdmk\jmxtools-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超详细解读

posted @ 2020-11-28 18:45  strongmore  阅读(1991)  评论(0编辑  收藏  举报