JMX超详细解读(转)

以你之姓@ 2022-08-29 11:57 474阅读 0赞

转自:https://www.cnblogs.com/dongguacai/p/5900507.html

一、JMX的定义  

  JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。这是官方文档上的定义,我看过很多次也无法很好的理解。我个人的理解是JMX让程序有被管理的功能,例如你开发一个WEB网站,它是在24小时不间断运行,那么你肯定会对网站进行监控,如每天的UV、PV是多少;又或者在业务高峰的期间,你想对接口进行限流,就必须去修改接口并发的配置值。

  应用场景:中间件软件WebLogic的管理页面就是基于JMX开发的,而JBoss则整个系统都基于JMX构架。

对于一些参数的修改,网上有一段描述还是比较形象的:

1、程序初哥一般是写死在程序中,到要改变的时候就去修改代码,然后重新编译发布。

2、程序熟手则配置在文件中(JAVA一般都是properties文件),到要改变的时候只要修改配置文件,但还是必须重启系统,以便读取配置文件里最新的值。

3、程序好手则会写一段代码,把配置值缓存起来,系统在获取的时候,先看看配置文件有没有改动,如有改动则重新从配置里读取,否则从缓存里读取。

4、程序高手则懂得物为我所用,用JMX把需要配置的属性集中在一个类中,然后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页,以方便我们对参数值进行修改。

二、JMX架构图:

7e5fe2ca0349f586385ff80bf801eac1.png

从图中我们可以看到,JMX的结构一共分为三层:

1、基础层:主要是MBean,被管理的资源。

MBean分为如下四种,我接下来主要介绍standard MBean
























类型 描述
standard MBean 这种类型的MBean最简单,它能管理的资源(包括属性,方法,时间)必须定义在接口中,然后MBean必须实现这个接口。它的命名也必须遵循一定的规范,例如我们的MBean为Hello,则接口必须为HelloMBean。
dynamic MBean 必须实现javax.management.DynamicMBean接口,所有的属性,方法都在运行时定义
open MBean 此MBean的规范还不完善,正在改进中
model MBean 与标准和动态MBean相比,你可以不用写MBean类,只需使用javax.management.modelmbean.RequiredModelMBean即可。RequiredModelMBean实现了ModelMBean接口,而ModelMBean扩展了DynamicMBean接口,因此与DynamicMBean相似,Model MBean的管理资源也是在运行时定义的。与DynamicMBean不同的是,DynamicMBean管理的资源一般定义在DynamicMBean中(运行时才决定管理那些资源),而model MBean管理的资源并不在MBean中,而是在外部(通常是一个类),只有在运行时,才通过set方法将其加入到model MBean中。后面的例子会有详细介绍

2、适配层:MBeanServer,主要是提供对资源的注册和管理。

3、接入层:提供远程访问的入口。

接下来我这里会用程序来介绍三种访问JMX的方式:

三、JDK的小工具Jconsole访问

1、 首先定义一个MBean接口,接口的命名规范为以具体的实现类为前缀(这个规范很重要)

复制代码

  1. 1 package jmx;
  2. 2
  3. 3 public interface HelloMBean
  4. 4 {
  5. 5 public String getName();
  6. 6
  7. 7 public void setName(String name);
  8. 8
  9. 9 public String getAge();
  10. 10
  11. 11 public void setAge(String age);
  12. 12
  13. 13 public void helloWorld();
  14. 14
  15. 15 public void helloWorld(String str);
  16. 16
  17. 17 public void getTelephone();
  18. 18 }

复制代码

2、定义一个实现类,实现上面的接口:

复制代码

  1. 1 package jmx;
  2. 2
  3. 3 /*
  4. 4 * 该类名称必须与实现的接口的前缀保持一致(即MBean前面的名称
  5. 5 */
  6. 6 public class Hello implements HelloMBean
  7. 7 {
  8. 8 private String name;
  9. 9
  10. 10 private String age;
  11. 11
  12. 12 public void getTelephone()
  13. 13 {
  14. 14 System.out.println("get Telephone");
  15. 15 }
  16. 16
  17. 17 public void helloWorld()
  18. 18 {
  19. 19 System.out.println("hello world");
  20. 20 }
  21. 21
  22. 22 public void helloWorld(String str)
  23. 23 {
  24. 24 System.out.println("helloWorld:" + str);
  25. 25 }
  26. 26
  27. 27 public String getName()
  28. 28 {
  29. 29 System.out.println("get name 123");
  30. 30 return name;
  31. 31 }
  32. 32
  33. 33 public void setName(String name)
  34. 34 {
  35. 35 System.out.println("set name 123");
  36. 36 this.name = name;
  37. 37 }
  38. 38
  39. 39 public String getAge()
  40. 40 {
  41. 41 System.out.println("get age 123");
  42. 42 return age;
  43. 43 }
  44. 44
  45. 45 public void setAge(String age)
  46. 46 {
  47. 47 System.out.println("set age 123");
  48. 48 this.age = age;
  49. 49 }
  50. 53 }

复制代码

3、定义agent层:

复制代码

  1. 1 package jmx;
  2. 2
  3. 3 import java.lang.management.ManagementFactory;
  4. 4
  5. 5 import javax.management.JMException;
  6. 6 import javax.management.MBeanServer;
  7. 7 import javax.management.ObjectName;
  8. 8
  9. 9 public class HelloAgent
  10. 10 {
  11. 11 public static void main(String[] args) throws JMException, Exception
  12. 12 {
  13. 13 MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  14. 14 ObjectName helloName = new ObjectName("jmxBean:name=hello");
  15. 15 //create mbean and register mbean
  16. 16 server.registerMBean(new Hello(), helloName);
  17. 17 Thread.sleep(60*60*1000);
  18. 18 }
  19. 19 }

复制代码

1、其中第13行是通过工厂类获取MBeanServer,用来做MBean的容器 。

2、第14行中的ObjectName中的取名是有一定规范的,格式为:“域名:name=MBean名称”,其中域名和MBean的名称可以任意取。这样定义后,就可以唯一标识我们定义的这个MBean的实现类了。

3、第16行是将Hello这个类注入到MBeanServer中,注入需要创建一个ObjectName类

这样,一个简单的JMX的DEMO已经写完了,现在我们通过JDK提供的Jconsole来进行操作。

1、首先在自己的本地路径下:C:\Program Files (x86)\Java\jdk1.6.0_43\bin找到jconsole.exe这个小工具,双击打开:

7cac8e5bcc60bac8aeedec977921d1b9.png

2、双击打开我们的本地进程:HelloAgent:

c97fc8fae6dccefbe68d8282b9ddb37c.png

3.在这个界面上,我们可以给程序中HelloMBean的属性赋值,也可以调用其中的方法:

b71a14bfcbfd0c6feed29456a24fe155.png

4、控制台打印如下:

3b6c235d5f50c9db0fda9401a14275a2.png

四、通过JMX提供的工具页访问

这里,我们复用上面的接口和实现类,只需要改动适配层,这里需要到导入外部jar包jdmk

复制代码

  1. package jmx;
  2. import java.lang.management.ManagementFactory;
  3. import javax.management.JMException;
  4. import javax.management.MBeanServer;
  5. import javax.management.ObjectName;
  6. import com.sun.jdmk.comm.HtmlAdaptorServer;
  7. public class HelloAgent
  8. {
  9. public static void main(String[] args) throws JMException, Exception
  10. {
  11. MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  12. ObjectName helloName = new ObjectName("jmxBean:name=hello");
  13. //create mbean and register mbean
  14. server.registerMBean(new Hello(), helloName);
  15. ObjectName adapterName = new ObjectName("HelloAgent:name=htmladapter,port=8082");
  16. HtmlAdaptorServer adapter = new HtmlAdaptorServer();
  17. server.registerMBean(adapter, adapterName);
  18. adapter.start();
  19. }
  20. }

复制代码

我们访问地址:http://localhost:8082,点击name=hello:

accbea96282920fa25265c2aba00fa59.png

2aafc9a0d3cfe39ff1af5f995ed487e0.png

1、在这里创建一个AdaptorServer,这个类将决定MBean的管理界面,这里用最普通的Html型界面。AdaptorServer其实也是一个MBean。

2、我们可以看到这个工具页,其实与我们上一个案例中的Jconsole中的管理界面类似,都可以操作资源中的属性和方法。

五、通过客户端程序进行远程访问

1、这里需要对agent进行修改,增加ip和porta绑定部分的逻辑

复制代码

  1. package jmxTest;
  2. import java.io.IOException;
  3. import java.lang.management.ManagementFactory;
  4. import java.rmi.RemoteException;
  5. import java.rmi.registry.LocateRegistry;
  6. import javax.management.JMException;
  7. import javax.management.MBeanServer;
  8. import javax.management.ObjectName;
  9. import javax.management.remote.JMXConnectorServer;
  10. import javax.management.remote.JMXConnectorServerFactory;
  11. import javax.management.remote.JMXServiceURL;
  12. public class HelloAgent
  13. {
  14. public static void main(String[] args) throws JMException, NullPointerException
  15. {
  16. MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  17. ObjectName helloName = new ObjectName("jmxBean:name=hello");
  18. //create mbean and register mbean
  19. server.registerMBean(new Hello(), helloName);
  20. try
  21. {
  22. //这个步骤很重要,注册一个端口,绑定url后用于客户端通过rmi方式连接JMXConnectorServer
  23. LocateRegistry.createRegistry(9999);
  24. //URL路径的结尾可以随意指定,但如果需要用Jconsole来进行连接,则必须使用jmxrmi
  25. JMXServiceURL url = new JMXServiceURL
  26. ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
  27. JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
  28. System.out.println("begin rmi start");
  29. jcs.start();
  30. System.out.println("rmi start");
  31. }
  32. catch (RemoteException e)
  33. {
  34. e.printStackTrace();
  35. }
  36. catch (IOException e)
  37. {
  38. e.printStackTrace();
  39. }
  40. }
  41. }

复制代码

写到这里,如果没有client进行远程连接,可以使用Jconsole进行远程访问:

9178b58cd6e9adcdb73688e934a75869.png

2、客户端Client程序,用于与agent进行远程连接:

复制代码

  1. 1 package jmx;
  2. 2
  3. 3 import java.io.IOException;
  4. 4
  5. 5 import javax.management.Attribute;
  6. 6 import javax.management.MBeanServerConnection;
  7. 7 import javax.management.MBeanServerInvocationHandler;
  8. 8 import javax.management.ObjectName;
  9. 9 import javax.management.remote.JMXConnector;
  10. 10 import javax.management.remote.JMXConnectorFactory;
  11. 11 import javax.management.remote.JMXServiceURL;
  12. 12
  13. 13
  14. 14 public class Client
  15. 15 {
  16. 16 public static void main(String[] args) throws IOException, Exception, NullPointerException
  17. 17 {
  18. 18 JMXServiceURL url = new JMXServiceURL
  19. 19 ("service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi");
  20. 20 JMXConnector jmxc = JMXConnectorFactory.connect(url,null);
  21. 21
  22. 22 MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
  23. 23 //ObjectName的名称与前面注册时候的保持一致
  24. 24 ObjectName mbeanName = new ObjectName("jmxBean:name=hello");
  25. 25
  26. 26 System.out.println("Domains ......");
  27. 27 String[] domains = mbsc.getDomains();
  28. 28
  29. 29 for(int i=0;i<domains.length;i++)
  30. 30 {
  31. 31 System.out.println("doumain[" + i + "]=" + domains[i] );
  32. 32 }
  33. 33
  34. 34 System.out.println("MBean count = " + mbsc.getMBeanCount());
  35. 35 //设置指定Mbean的特定属性值
  36. 36 //这里的setAttribute、getAttribute操作只能针对bean的属性
  37. 37 //例如对getName或者setName进行操作,只能使用Name,需要去除方法的前缀
  38. 38 mbsc.setAttribute(mbeanName, new Attribute("Name","杭州"));
  39. 39 mbsc.setAttribute(mbeanName, new Attribute("Age","1990"));
  40. 40 String age = (String)mbsc.getAttribute(mbeanName, "Age");
  41. 41 String name = (String)mbsc.getAttribute(mbeanName, "Name");
  42. 42 System.out.println("age=" + age + ";name=" + name);
  43. 43
  44. 44 HelloMBean proxy = MBeanServerInvocationHandler.
  45. 45 newProxyInstance(mbsc, mbeanName, HelloMBean.class, false);
  46. 46 proxy.helloWorld();
  47. 47 proxy.helloWorld("migu");
  48. 48 proxy.getTelephone();
  49. 49 //invoke调用bean的方法,只针对非设置属性的方法
  50. 50 //例如invoke不能对getName方法进行调用
  51. 51 mbsc.invoke(mbeanName, "getTelephone", null, null);
  52. 52 mbsc.invoke(mbeanName, "helloWorld",
  53. 53 new String[]{"I'll connect to JMX Server via client2"}, new String[]{"java.lang.String"});
  54. 54 mbsc.invoke(mbeanName, "helloWorld", null, null);
  55. 55 }
  56. 56 }

复制代码

a、在35到41行,是对属性进行赋值和取值,这里我们不能直接调用方法,而是通过setAttribute、getAttrubute方法来进行操作,则属性的首字母要大写。

b、对资源里面的方法进行操作有两种方式:一是通过代理直接调用方法;二是通过JAVA的反射注入的方式进行方法的调用。

下面我们来看看执行结果,先执行agent,再执行客户端:

c、client的控制台打印结果:

83ab859f81c0f1d8d7d8ad6e3a2a68e1.png

d、agent控制台打印结果:

e8130d21f605c69b99d92de4bc9c5a54.png

六、Notification

MBean之间的通信是必不可少的,Notification就起到了在MBean之间沟通桥梁的作用。JMX 的通知由四部分组成:

1、Notification这个相当于一个信息包,封装了需要传递的信息

2、Notification broadcaster这个相当于一个广播器,把消息广播出。

3、Notification listener 这是一个监听器,用于监听广播出来的通知信息。

4、Notification filiter 这个一个过滤器,过滤掉不需要的通知。这个一般很少使用。

这里我们使用日常打招呼的场景:jack与我偶遇,jack说:hi;我礼貌的回答:hello,jack。

这里我们先分别创建两个资源:

复制代码

  1. package jmx;
  2. /*
  3. * 该类名称必须与实现的接口的前缀保持一致(即MBean前面的名称
  4. */
  5. public class Hello implements HelloMBean
  6. {
  7. private String name;
  8. public String getName()
  9. {
  10. return name;
  11. }
  12. public void setName(String name)
  13. {
  14. this.name = name;
  15. }
  16. public void printHello()
  17. {
  18. System.out.println("Hello World, " + name);
  19. }
  20. public void printHello(String whoName)
  21. {
  22. System.out.println("Hello , " + whoName);
  23. }
  24. }

复制代码

复制代码

  1. package jmx;
  2. /*
  3. * 接口名必须以MBean结尾
  4. */
  5. public interface HelloMBean
  6. {
  7. public String getName();
  8. public void setName(String name);
  9. public void printHello();
  10. public void printHello(String whoName);
  11. }

复制代码

复制代码

  1. package jmx;
  2. import javax.management.Notification;
  3. import javax.management.NotificationBroadcasterSupport;
  4. public class Jack extends NotificationBroadcasterSupport implements JackMBean
  5. {
  6. private int seq = 0;
  7. public void hi()
  8. {
  9. //创建一个信息包
  10. Notification notify =
  11. //通知名称;谁发起的通知;序列号;发起通知时间;发送的消息
  12. new Notification("jack.hi",this,++seq,System.currentTimeMillis(),"jack");
  13. sendNotification(notify);
  14. }
  15. }

复制代码

  1. package jmx;
  2. public interface JackMBean
  3. {
  4. public void hi();
  5. }

这里的类Jack不仅实现了MBean接口,还继承了NotificationBroadcasterSupport。jack在这里创建并发送了一个消息包。

复制代码

  1. package jmx;
  2. import javax.management.Notification;
  3. import javax.management.NotificationListener;
  4. public class HelloListener implements NotificationListener
  5. {
  6. public void handleNotification(Notification notification, Object handback)
  7. {
  8. if(handback instanceof Hello)
  9. {
  10. Hello hello = (Hello)handback;
  11. hello.printHello(notification.getMessage());
  12. }
  13. }
  14. }

复制代码

复制代码

  1. package jmx;
  2. import java.lang.management.ManagementFactory;
  3. import javax.management.JMException;
  4. import javax.management.MBeanServer;
  5. import javax.management.ObjectName;
  6. public class HelloAgent
  7. {
  8. public static void main(String[] args) throws JMException, Exception
  9. {
  10. MBeanServer server = ManagementFactory.getPlatformMBeanServer();
  11. ObjectName helloName = new ObjectName("yunge:name=Hello");
  12. Hello hello=new Hello();
  13. server.registerMBean(hello, helloName);
  14. Jack jack = new Jack();
  15. server.registerMBean(jack, new ObjectName("jack:name=Jack"));
  16. jack.addNotificationListener(new HelloListener(), null, hello);
  17. Thread.sleep(500000);
  18. }
  19. }

复制代码

我们用Jconsole来进行访问:

f2f303cb7b23a86531d618cb68806470.png

这里我们可以看到有两个MBean,一个是yunge,一个是jack。我们执行jack的hi方法后,去看下控制台上的打印信息;

d3a5137cfb78a96792834fffb18741f5.png

七、linux下利用JMX监控Tomcat

  利用JMX监控Tomcat,就是相当于部署在tomcat上的应用作为服务端,也就是被管理资源的对象。然后通过程序或者jconsole远程连接到该应用上来。远程连接需要服务器端提供ip和port。如果需要加密访问的话,还需要配置用户名、密码等参数。

主要是在tomcat下的文件catalina.sh中进行一些环境变量的配置配置:

32ee934a5627221e1091c853620432bd.png

-Dcom.sun.management.jmxremote=true 相关 JMX 代理侦听开关

-Djava.rmi.server.hostname 服务器端的IP
-Dcom.sun.management.jmxremote.port=29094 相关 JMX 代理侦听请求的端口

-Dcom.sun.management.jmxremote.ssl=false 指定是否使用 SSL 通讯

-Dcom.sun.management.jmxremote.authenticate=false 指定是否需要密码验证

这样就可以通过客户端或者jconsole对tomcat进行监控。

发表评论

表情:
评论列表 (有 0 条评论,474人围观)

还没有评论,来说两句吧...

相关阅读

    相关 MemCache详细解读

    MemCache是什么? MemCache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载。它通过

    相关 []详细解读TrueSkill 排名系统

    概要 大多数竞技游戏都有一个评价玩家是否完成目标的度量指标,它是游戏的基础。对于包含两个或两个以上玩家(多玩家比赛)的比赛,常涉及到游戏玩家技能的排名方法。游戏鼓励玩家之间相