20-Spring JMX
2018-10-06 08:06:05来源:博客园 阅读 ()
基本概念
JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。简介看上去不是很直观和明白,也可能我了解的太少,理解还不够深入。那么在本例中,主要是介绍通过使用Spring JMX来管理和修改运行中的应用程序的配置。关于更多的JMX概念,大家可以自行搜索。
将Spring Bean导出为MBean
通过MBeanExporter将普通的Spring Bean导出为MBean,Spring 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=SpittleController,spitter是管理域的名称,接下来就是key=value一个键值对,最终在JMX管理工具中,看到的MBean名字就是SpittleController。然后我们启动Tomcat服务,就可以使用JConsole来查看和修改SpittleController这个MBean了。
通过JConsole,可以看到SpittleController的属性和方法。为了看到动态修改属性的效果,我们把PageSize改为50,然后可以通过浏览器,访问SpittleController的test方法所对应的URL,也可以通过JConsole操作界面来调用test方法,来查看PageSize属性的变化。
下面控制台中的PageSize值,是在JConsole中修改属性值前后的输出结果
有时候我们希望通过程序,来访问和操作远程服务器端的MBean,这时候我们就需要创建和访问远程MBean了。
暴露远程MBean
使MBean成为远程对象的最简单方式是配置Spring的ConnectorServerFactoryBean,并为其设置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 }
ConnectorServerFactoryBean的serviceURL属性指明了通过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这个MBean的PageSize属性,该属性的首字母原本是小写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 }
notificationPublisher的sendNotification方法用于发送消息通知,接下来我们还需要建立一个消息监听器。接收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
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- Disruptor 基础篇 2020-04-13
- 记忆重拾:排序技术-排序的基本概念与性能 2020-03-09
- 并发与多线程 2019-12-23
- JVM参数及调优 2019-11-07
- 微服务与Spring Cloud基本概念、Spring Cloud版本命名方式与 2019-10-29
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash