1. 程式人生 > 實用技巧 >JMX簡單入門

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,這是一個監聽器,用於監聽廣播出來的通知資訊。
  • Notification filiter 這個一個過濾器,過濾掉不需要的通知。

定義訊息廣播器,也是一個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超詳細解讀