zoukankan      html  css  js  c++  java
  • Spring JMX之二:远程访问MBean(spring通过annotation暴露MBean)

    虽然最初的JMX规范提及了通过MBean进行应用的远程管理,但是它并没有定义实际的远程 访问协议或API。因此,会由JMX供应商定义自己的JMX远程访问解决方案,但这通常又是专 有的。

    为了满足以标准方式进行远程访问JMX的需求,JCP(Java Community Process)制订了JSR-160: Java管理扩展远程访问API规范(Java Management Extensions Remote API Specification)。该规范 定义了JMX远程访问的标准,该标准至少需要绑定RMI和可选的JMX消息协议(JMX Messaging Protocol ,JMXMP)。

    在本小节中,我们将看到Spring如何远程访问MBean。我们首先从配置Spring把 SpittleController导出为远程MBean开始,然后我们再了解如何使用Spring远程操纵 MBean。

    spring通过annotation暴露MBean

    在javaconfig类中开启注解

    @EnableMBeanExport

    涉及到三个重要的annotation:@ManagedResource @ManagedAttribute 和 @ManagedOperation。

    说明:  

    1. @ManagedResource @ManagedAttribute 和 @ManagedOperation 还有许多参数,具体使用请参考spring官方手册。( spring手册[2.5.3] ---- 20.3.4. 源代码级的元数据类型)
    2. @ManagedOperationParameters 是对@ManagedOperation的补充。具体看代码样例中的add1方法上的注解,然后再看图2(查看已暴露MBean的方法)的add1方法和add2的区别。添加参数说明的add1方法会显示出参数名,而add2方法则是默认的参数名p1/p2。
    3. 没有添加@ManagedOperation和@ManagedAttribute的方法,在图2中就没有看到,说明添加了注解的方法暴露MBean是可用的。
    4. @ManagedOperation和@ManagedAttribute的区别,请查看2.1的详解。

    1、暴露远程MBean

    使MBean成为远程对象的最简单方式是配置Spring的ConnectorServerFactoryBean:

        @Bean
        public ConnectorServerFactoryBean connectorServerFactoryBean() {
            return new ConnectorServerFactoryBean();
        }

    ConnectorServerFactoryBean会创建和启动JSR-160 JMXConnectorServer。默认 情况下,服务器使用JMXMP协议并监听端口9875——因此,它将绑 定“service:jmx:jmxmp://localhost:9875”。但是我们导出MBean的可选方案并不局限于JMXMP。

    根据不同JMX的实现,我们有多种远程访问协议可供选择,包括远程方法调用(Remote Method Invocation,RMI)、SOAP、Hessian/Burlap和IIOP(Internet InterORB Protocol)。为MBean绑定不同 的远程访问协议,我们仅需要设置ConnectorServerFactoryBean的serviceUrl属性。 例如,如果我们想使用RMI远程访问MBean,我们可以像下面示例这样配置:

        @Bean
        public ConnectorServerFactoryBean connectorServerFactoryBean() {
            ConnectorServerFactoryBean csfb = new ConnectorServerFactoryBean();
            csfb.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/spitter");
            return csfb;
        }

    在这里,我们将ConnectorServerFactoryBean绑定到了一个RMI注册表,该注册表监听 本机的1099端口。这意味着我们需要一个RMI注册表运行时,并监听该端口。我们可以回顾下第15章,RmiServiceExporter可以为我们自动启动一个RMI注册表。但是,我们在本示例 中不使用RmiServiceExporter,而是通过在Spring中声明RmiRegistryFactoryBean 来启动一个RMI注册表,如下面的@Bean方法所示:

        @Bean
        public RmiRegistryFactoryBean rmiRegistryFB() {
            RmiRegistryFactoryBean rmiRegistryFB = new RmiRegistryFactoryBean();
            rmiRegistryFB.setPort(1099);
            return rmiRegistryFB;
        }

    没错!现在我们的MBean可以通过RMI进行远程访问了。

    完整代码如下:

    package com.dxz.mvcdemo1.web.jmx4;
    
    import static org.springframework.web.bind.annotation.RequestMethod.GET;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.jmx.export.annotation.ManagedAttribute;
    import org.springframework.jmx.export.annotation.ManagedResource;
    import org.springframework.jmx.support.ConnectorServerFactoryBean;
    import org.springframework.remoting.rmi.RmiRegistryFactoryBean;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/biz4")
    @ManagedResource(objectName="spitter:name=SpittleController4") //将SpittleController导出为MBean
    public class SpittleCntroller4 {
    
        // 默认每个页面的大小
        public static final int DEFAULT_SPITTLES_PER_PAGE = 25;
    
        // 每页的大小
        private int spittlesPerPage = DEFAULT_SPITTLES_PER_PAGE;
    
        //@ManagedOperation@ManagedOperation注解替换@ManagedAttribute注解来标注存取器方法
        @ManagedAttribute        //将spittlesPerPage暴露为托管属性
        public int getSpittlesPerPage() {
            return spittlesPerPage;
        }
        
        //@ManagedOperation@ManagedOperation注解替换@ManagedAttribute注解来标注存取器方法
        @ManagedAttribute        //将spittlesPerPage暴露为托管属性
        public void setSpittlesPerPage(int spittlesPerPage) {
            this.spittlesPerPage = spittlesPerPage;
        }
    
        @RequestMapping(value = "/test4", method = GET)
        public String test() {
            String result = spittlesPerPage + " - test()";
            System.out.println(result);
            return "home";
        }
    
        //启动一个RMI注册表,并监听1099端口
        @Bean
        public RmiRegistryFactoryBean rmiRegistryFB() {
            RmiRegistryFactoryBean rmiRegistryFB = new RmiRegistryFactoryBean();
            rmiRegistryFB.setPort(1099);
            return rmiRegistryFB;
        }
        
        //将ConnectorServerFactoryBean绑定到了上面的RMI注册表,该注册表监听本机的1099端口
        @Bean
        public ConnectorServerFactoryBean connectorServerFactoryBean() {
            ConnectorServerFactoryBean csfb = new ConnectorServerFactoryBean();
            csfb.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/spitter");
            return csfb;
        }
    
    }

    启动测试:

    注意:日志中RMI注册表运行了,并监听1099端口

    下面先通过java自带的工具连接及测试如下,选择“远程进程”并将代码中的url=“service:jmx:rmi://localhost/jndi/rmi://localhost:1099/spitter”填入输入框中,进行连接:

    连接成功后的结果如下:

    但是如果没有人通过RMI访问MBean 的话,那就不值得这么做。所以现在让我们把关注点转向JMX远程访问的客户端,看看如何在 Spring中装配一个远程MBean到JMX客户端中。

    2 访问远程MBean

    要想访问远程MBean服务器,我们需要在Spring上下文中配置MbeanServerConnectionFactoryBean。下面的bean声明装配了一个MbeanServerConnectionFactoryBean,该bean用于访问我们在上一节中所创建的基于RMI的远程服务器。

        @Bean
        public MBeanServerConnectionFactoryBean connectionFactoryBean() throws MalformedURLException {
            MBeanServerConnectionFactoryBean mbscfb = new MBeanServerConnectionFactoryBean();
            mbscfb.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/spitter");
            return mbscfb;
        }

    顾名思义,MBeanServerConnectionFactoryBean是一个可用于创建MbeanServerConnection的工厂bean。由MBeanServerConnectionFactoryBean所生成的 MBeanServerConnection实际上是作为远程MBean服务器的本地代理。它能够以 MBeanServerConnection的形式注入到其他bean的属性中:

        @Bean
        public JmxClient jmxClient(MBeanServerConnection connection) {
            JmxClient jmxClient = new JmxClient();
            jmxClient.setMbeanServerConnection(connection);
            return jmxClient;
        }

    JmxClient.java

    package com.dxz.mvcdemo2.web;
    
    import javax.management.MBeanServerConnection;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class JmxClient {
    
        MBeanServerConnection mbeanServerConnection;
        
        public JmxClient() {
            
        }
        
        public MBeanServerConnection getMbeanServerConnection() {
            return mbeanServerConnection;
        }
        public void setMbeanServerConnection(MBeanServerConnection mbeanServerConnection) {
            this.mbeanServerConnection = mbeanServerConnection;
        }

    测试类:

    package com.dxz.mvcdemo2.web;
    
    import static org.springframework.web.bind.annotation.RequestMethod.GET;
    
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.util.Set;
    
    import javax.management.AttributeNotFoundException;
    import javax.management.InstanceNotFoundException;
    import javax.management.MBeanException;
    import javax.management.MBeanServerConnection;
    import javax.management.MalformedObjectNameException;
    import javax.management.ObjectName;
    import javax.management.ReflectionException;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.jmx.support.MBeanServerConnectionFactoryBean;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/biz4")
    public class ClientController {
    
        @Autowired
        JmxClient jmxClient;
    
        @RequestMapping(value = "/client4", method = GET)
        public String test() throws IOException, MalformedObjectNameException, AttributeNotFoundException,
                InstanceNotFoundException, MBeanException, ReflectionException {
            MBeanServerConnection connection = jmxClient.mbeanServerConnection;
            int mbenaCount = connection.getMBeanCount();
            System.out.println("There are " + mbenaCount + " MBeans");
    
            ObjectName managerObjName = new ObjectName("spitter:name=SpittleController4");
            Set<ObjectName> s = connection.queryNames(managerObjName, null);
            for (ObjectName obj : s) {
                System.out.println(obj);
            }
    
            Object cronExpression = connection.getAttribute(new ObjectName("spitter:name=SpittleController4"),
                    "SpittlesPerPage");
            System.out.println(cronExpression);
            return "home";
        }
        @Bean
        public MBeanServerConnectionFactoryBean connectionFactoryBean() throws MalformedURLException {
            MBeanServerConnectionFactoryBean mbscfb = new MBeanServerConnectionFactoryBean();
            mbscfb.setServiceUrl("service:jmx:rmi://localhost/jndi/rmi://localhost:1099/spitter");
            return mbscfb;
        }
        
        @Bean
        public JmxClient jmxClient(MBeanServerConnection connection) {
            JmxClient jmxClient = new JmxClient();
            jmxClient.setMbeanServerConnection(connection);
            return jmxClient;
        }
    }

    结果:

    3、代理MBean

  • 相关阅读:
    Fish
    Brackets
    StoneWall【★★★★★】
    Nesting
    ajax补充FormData
    初始Ajax
    extra过滤
    Django 之缓存
    django中的信号
    Form组件归类
  • 原文地址:https://www.cnblogs.com/duanxz/p/3977746.html
Copyright © 2011-2022 走看看