当前位置: 移动技术网 > IT编程>开发语言>Java > 20-Spring JMX

20-Spring JMX

2018年10月04日  | 移动技术网IT编程  | 我要评论

基本概念

jmxjava management extensions,即java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。jmx可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。简介看上去不是很直观和明白,也可能我了解的太少,理解还不够深入。那么在本例中,主要是介绍通过使用spring jmx来管理和修改运行中的应用程序的配置。关于更多的jmx概念,大家可以自行搜索。

将spring bean导出为mbean

通过mbeanexporter将普通的spring bean导出为mbeanspring bean中的属性就会变成mbean的托管属性,因此,我们就可以在程序运行时,对该属性进行修改。如下代码

 1 @controller
 2 @requestmapping("/biz")
 3 public class spittlecontroller {
 4     private int pagesize = 10;
 5 
 6     public int getpagesize() {
 7         return pagesize;
 8     }
 9 
10     public void setpagesize(int pagesize) {
11         this.pagesize = pagesize;
12     }
13 
14     @requestmapping(value = "/test")
15     public string test() {
16         system.out.println("pagesize="+pagesize);
17         return "index";
18     }
19 
20 }

 

 1 @configuration
 2 @componentscan("spittle.controller")
 3 public class mbeanconfig {
 4 
 5     @bean
 6     public mbeanexporter mbeanexporter(spittlecontroller spittlecontroller) {
 7         system.out.println("mbeanconfig mbeanexporter");
 8         mbeanexporter exporter = new mbeanexporter();
 9         map<string, object> beans = new hashmap<>();
10         beans.put("spitter:name=spittlecontroller", spittlecontroller);
11         exporter.setbeans(beans);
12         return exporter;
13     }
14 }

配置mbeanexporter最简单的方式为其它的beans属性设置一个map集合,集合中的元素就是我们希望导出为mbean的一个或多个spring bean。本例中,我们希望将spittlecontroller导出为mbean,并为它指定一个名字spitter:name=spittlecontrollerspitter是管理域的名称,接下来就是key=value一个键值对,最终在jmx管理工具中,看到的mbean名字就是spittlecontroller。然后我们启动tomcat服务,就可以使用jconsole来查看和修改spittlecontroller这个mbean了。

 通过jconsole,可以看到spittlecontroller的属性和方法。为了看到动态修改属性的效果,我们把pagesize改为50,然后可以通过浏览器,访问spittlecontrollertest方法所对应的url,也可以通过jconsole操作界面来调用test方法,来查看pagesize属性的变化。

下面控制台中的pagesize值,是在jconsole中修改属性值前后的输出结果

有时候我们希望通过程序,来访问和操作远程服务器端的mbean,这时候我们就需要创建和访问远程mbean了。

暴露远程mbean

使mbean成为远程对象的最简单方式是配置springconnectorserverfactorybean,并为其设置serviceurl属性。我们来看一下如下代码

 1 @configuration
 2 @componentscan("spittle.controller")
 3 public class mbeanconfig {
 4 
 5     @bean
 6     public mbeanexporter mbeanexporter(spittlecontroller spittlecontroller) {
 7         system.out.println("mbeanconfig mbeanexporter");
 8         mbeanexporter exporter = new mbeanexporter();
 9         map<string, object> beans = new hashmap<>();
10         beans.put("spitter:name=spittlecontroller", spittlecontroller);
11         exporter.setbeans(beans);
12         return exporter;
13     }
14 
15     @bean(name="rmiregistryfb")
16     public rmiregistryfactorybean rmiregistryfb() {
17         system.out.println("rmiregistryfb");
18         rmiregistryfactorybean rmiregistryfb = new rmiregistryfactorybean();
19         rmiregistryfb.setport(1098);
20         return rmiregistryfb;
21     }
22 
23     @bean
24     @dependson("rmiregistryfb")
25     public connectorserverfactorybean connectorserverfactorybean() {
26         system.out.println("connectorserverfactorybean");
27         connectorserverfactorybean csfb = new connectorserverfactorybean();
28         string serviceurl = "service:jmx:rmi://localhost/jndi/rmi://localhost:1098/spitter";
29         csfb.setserviceurl(serviceurl);
30         return csfb;
31     }
32 
33 }

connectorserverfactorybeanserviceurl属性指明了通过rmi协议来访问远程mbean,并绑定到本机1098端口的一个rmi注册表。因此,我们还需要一个监听该端口的rmi注册表对象,这正是rmiregistryfb的作用。注意观察,就会发现connectorserverfactorybean多了一个@dependson("rmiregistryfb")注解,从字面意思来看,该bean依赖于rmiregistryfb,由于spring在初始化bean的时候是无序加载,如果先加载了connectorserverfactorybean就会报错,所以就要先加载rmiregistryfb,再加载connectorserverfactorybean。现在我们的mbean可以通过rmi进行远程访问了。接下来我们来看看如何访问远程mbean

访问远程mbean

要想访问远程mbean服务器,我们需要在spring上下文中配置mbeanserverconnectionfactorybean。该bean用于访问我们在上一节中所创建的基于rmi的远程服务器。

 1 public class mbeanclientconfig {
 2 
 3     @bean
 4     public mbeanserverconnectionfactorybean connectionfactorybean() {
 5         system.out.println("connectionfactorybean");
 6         mbeanserverconnectionfactorybean mbscfb = new mbeanserverconnectionfactorybean();
 7         try {
 8             string serviceurl = "service:jmx:rmi://localhost/jndi/rmi://localhost:1098/spitter";
 9             mbscfb.setserviceurl(serviceurl);
10         } catch (malformedurlexception e) {
11             e.printstacktrace();
12         }
13         return mbscfb;
14     }
15 
16 }

现在,让我们编写一个测试类,来访问远程mbean,并动态修改它的属性吧。

 1 @runwith(springjunit4classrunner.class)
 2 @contextconfiguration(classes=mbeanclientconfig.class)
 3 public class mbeanclienttest {
 4 
 5     @autowired
 6     mbeanserverconnection mbeanserverconnection;
 7 
 8     @test
 9     public void mbeanservertest()  {
10         objectname name = new objectname("spitter:name=spittlecontroller");
11         set<objectname> mbeannames = mbeanserverconnection.querynames(name, null);
12         iterator<objectname> iter = mbeannames.iterator();
13         while(iter.hasnext()) {
14             objectname objectname = iter.next();
15             system.out.println(objectname.tostring());
16         }
17         mbeanserverconnection.setattribute(name, new attribute("pagesize", 20));
18         object cronexpression = mbeanserverconnection.getattribute(name,"pagesize");
19         system.out.println("pagesize="+cronexpression.tostring());
20     }
21 
22 }

测试类中,我们注入了一个mbeanserverconnection,它是mbeanserverconnectionfactorybean的一个对象,我们通过该对象,查找并修改前面我们创建的spittlecontroller这个mbeanpagesize属性,该属性的首字母原本是小写pagesize,当我们需要对它进行修改的时候,就要使用首字母大写的形式pagesize

处理消息通知

通过查询mbean获得信息只是查看应用状态的一种方法。但当应用发生重要事件时,如果希望能够及时告知我们,这通常不是最有效的方法。jmx通知(jmx notification)是mbean与外部世界主动通信的一种方法,而不是等待外部应用对mbean进行查询以获得信息。spring通过notificationpublisheraware接口提供了发送通知的支持。任何希望发送通知的mbean都必须实现这个接口。例如,请查看如下程序清单

 1 @component
 2 @managednotification(notificationtypes = "spittlenotifier.onemillionspittles",name="todo")
 3 public class spittlenotifier implements notificationpublisheraware{
 4 
 5     private notificationpublisher notificationpublisher;
 6     //注入notificationpublisher
 7     @override
 8     public void setnotificationpublisher(notificationpublisher notificationpublisher) {
 9         this.notificationpublisher = notificationpublisher;
10     }
11 
12     /**
13      * 发送消息通知
14      */
15     public void millionthspittleposted() {
16         notificationpublisher.sendnotification(
17                 new notification("spittlenotifier.onemillionspittles", this, 0, "this is test message"));
18     }
19 }

notificationpublishersendnotification方法用于发送消息通知,接下来我们还需要建立一个消息监听器。接收mbean通知的标准方法是实现notificationlistener接口,我们需要编写一个实现了该接口的类,并重写它的handlenotification方法。

 1 public class spittlenotificationlistener implements notificationlistener {
 2 
 3     /**
 4      * 处理消息通知
 5      * @param notification
 6      * @param handback
 7      */
 8     @override
 9     public void handlenotification(notification notification, object handback) {
10         string message = notification.getmessage();
11         system.out.println("receive message="+message);
12     }
13 }

消息通知和消息监听都已经创建好了。是时候给他们两个建立联系了,不然消息通知发出去之后,我们怎么知道谁来接收消息呢?

 1 @configuration
 2 @componentscan("spittle.notifier")
 3 public class notifierconfig {
 4 
 5     @bean
 6     public mbeanexporter mbeanexporter2(spittlenotifier spittlenotifier) {
 7         system.out.println("notifierconfig mbeanexporter");
 8         mbeanexporter exporter = new mbeanexporter();
 9         map<string, object> beans = new hashmap<>();
10         beans.put("spitter:name=spitternotifier", spittlenotifier);
11         exporter.setbeans(beans);
12         map<string, notificationlistener> mappings = new hashmap<>();
13         mappings.put("spitter:name=spitternotifier", new spittlenotificationlistener());
14         exporter.setnotificationlistenermappings(mappings);
15         return exporter;
16     }
17 }

类似前面导出mbean的代码,这里我们把spitternotifier导出为mbean,并为该mbean添加一个监听器。这样,spitternotifier就和spittlenotificationlistener建立起联系了。注意mappings.put这一行,这里面的key要和beans.put这一行的key保持一致,beans.put是为mbean指定一个名字,mappings.put是为指定的mbean添加监听器,如果这两个名字对不上,程序就会报错。最后,我们将消息通知类注入到我们的测试类中,就可以测试消息通知了。

 1 @controller
 2 @requestmapping("/notifier")
 3 public class notifiercontroller {
 4 
 5     @autowired
 6     private spittlenotifier spittlenotifier;
 7 
 8     @requestmapping(value = "/test")
 9     public string test() {
10         spittlenotifier.millionthspittleposted();
11         return "index";
12     }
13 
14 }

加载mbean配置类

前面我们已经有了的远程mbean,消息通知mbean,但是当tomcat启动的时候,我们还需要去加载它们,不然这些mbean仍然是访问不了的。以往我们都在web.xml中去,现在我们通过web.xml的替代方案webinitializer来加载。

 1 public class webinitializer extends abstractannotationconfigdispatcherservletinitializer {
 2 
 3     @override
 4     protected class<?>[] getrootconfigclasses() {
 5         return new class<?>[] { };
 6     }
 7 
 8     @override
 9     protected class<?>[] getservletconfigclasses() {
10         return new class<?>[] { mbeanconfig.class, notifierconfig.class };
11     }
12 
13     @override
14     protected string[] getservletmappings() {
15         return new string[] { "/" };
16     }
17 
18     @override
19     protected void customizeregistration(dynamic registration) {
20         registration.setasyncsupported(true);
21     }
22     
23 }

我们用这些代码替代原本要配在web.xml中配置的dispatcherservlet,然后再getservletconfigclasses方法中,去加载远程mbean,以及消息通知mbean。这样我们就可以访问mbean了。

可以在这里下载完整的代码:https://files.cnblogs.com/files/jkfd/springjmx-demo.zip

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网