Spring整合JMX

Spring整合JMX
一、优点

1、可以自动探测实现MBean接口的MBean对象,而且可以将一个普通的Spring Bean注册为MBean;

2、定制管理MBean的接口,根据需要暴露特定管理MBean的操作;

3、使用注解定义MBean管理接口;

4、可以实现对本地和远程MBean的代理。

二、实现方式
基于配置文件的实现
  • 标准MBEAN
  •     定义接口
       
    XXXMBean(接口名以MBean结尾,接口中暴露了对外的方法) 

        接口实现
       
    XXX(注意实现的名称和接口之间的关系)

        配置文件
       
        <?xml version="1.0" encoding="UTF-8"?>
    <beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">              
        <!--MBean Server初始化 -->
        <bean id="mbeanServer" class="org.springframework.jmx.export.MBeanExporter">
            <!--MBean的探测策略,这里是AUTODETECT_ALL自动探测,默认值为AUTODETECT_ALL,所以这里可以不用配置 -->
            <property name="autodetectModeName">
                <value>AUTODETECT_ALL</value>
            </property>
            <!-- 如果不使用自动探测 ,这里是手动注册的配置 -->
            <property name="beans">
                <map>
                    <entry key="MBEAN_NAME" value-ref="xxx"/>
                </map>
            </property>
        </bean>
        <!--MBean初始化 其中的name="mydomain:xxx=XxxMBean"可以根据需要自行定义-->
        <bean id="xxx" class="com.sinosoft.one.demo.jmx.Xxx"/>
        <bean name="testMBean:xxx=XxxMBean" class="com.sinosoft.one.demo.jmx.XXX">
            <!--实现类中一些需要初始化的属性 -->
            <property name="propertyOne" value="val1"/>
            <property name="propertyTwo" value="val2"/>
        </bean>
    </beans>
        


    对于探测模式autodetectModeName属性,Spring提供了4个取值:

    AUTODETECT_NONE 不启用自动探测,需要手动向MBean Server进行注册,即通过MBeanExporter的beans属性进入注册;
    AUTODETECT_MBEAN 在当前IOC容器中进行查找MBean组件;
    AUTODETECT_ASSEMBLER 设置根据MBeanInfoAssembler的策略进行探测;
    AUTODETECTALL 自动探测,是AUTODETECTMBEAN和AUTODETECT_ASSEMBLER的并集。
  • 普通Bean注册为MBean
  • Bean的实现方式没什么需要特别注意的,主要是配置文件中的配置。
          配置文件
          
    <?xml version="1.0" encoding="UTF-8"?>
    <beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">              
        <!--MBean Server初始化 -->
        <bean id="mbeanServer" class="org.springframework.jmx.export.MBeanExporter">
            <!--MBean的探测策略,这里是AUTODETECT_ALL自动探测,默认值为AUTODETECT_ALL,所以这里可以不用配置 -->
            <property name="autodetectModeName">
                <value>AUTODETECT_ALL</value>
            </property>
            <!-- 对于普通的Bean ,需要在这里手动注册 -->
            <property name="beans">
                <map>
                    <entry key="MBEAN_NAME" value-ref="xxx"/>
                </map>
            </property>
        </bean>
        <!--普通Spring Bean初始化-->
        <bean id="xxx" class="com.sinosoft.one.demo.jmx.Xxx"/>
    </beans> 
           

    因为普通的Bean没有对应的MBean接口,所以默认情况下,该类中public的成员都会暴露出来,通过MBean Server可以管理。实际上,系统中MBean的某些属性或方法可能不需要暴露给外部进行管理,为了克服这种缺点,Spring提供了基于方法列表和接口定制的功能,可以将你所感兴趣的属性或方法暴露给外部管理。

          管理暴露的方法
                方法一:配置方法列表
          
    <!-- 定义assembler -->    
    <bean id="assembler" class="org.springframework.jmx.export.
                assembler.MethodNameBasedMBeanInfoAssembler">
        <!--配置方法列表 -->
        <property name="managedMethods"  value="display" />
    </bean>     

                方法二:接口定制
          
    package com.sinosoft.one.demo.jmx.interface;
    /**
     *定制对外方法的接口 
     */
    public interface MethodManagerInterface {
        public String display(String arguments);
    }
    
    
    <!-- 定义assembler -->    
    <bean id="assembler" class="org.springframework.jmx.export.
            assembler.MethodNameBasedMBeanInfoAssembler">
        <!--配置方法列表 -->
        <property name="managedInterfaces" >
            <list>
                <value>
                    com.sinosoft.one.demo.MethodManagerInterface
                </value>    
            </list>
        </property> 
    </bean>
         

    最后,在bean mbeanServer中添加assembler属性
    <property name="assembler" ref="assembler" />
  • 基于注解的实现
  • 下边是一个实现例子
     package com.sinosoft.one.demo.jmx;
    
        import org.apache.log4j.Level;
        import org.apache.log4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.jmx.export.annotation.ManagedAttribute;
        import org.springframework.jmx.export.annotation.ManagedOperation;
        import org.springframework.jmx.export.annotation.ManagedOperationParameter;
        import org.springframework.jmx.export.annotation.ManagedOperationParameters;
        import org.springframework.jmx.export.annotation.ManagedResource;
    
        /**
         * JMX实现的动态控制日志级别
         * 
         * @author gwt
         */
        @ManagedResource(objectName = LoggerManager.MBEAN_NAME, description = "It's a program to manage log4j ")
        public class LoggerManager {
            /**
             * MBean Name
             */
            public static final String MBEAN_NAME = "log4j:logger=Log4j";
    
            /**
             * 用来记录日志信息
             */
            private static org.slf4j.Logger managerLogger = LoggerFactory
                    .getLogger(LoggerManager.class);
    
            /**
             * 获取日志的默认级别
             * 
             * @return
             */
            @ManagedAttribute
            public String getRootLoggerLevel() {
                Logger logger = Logger.getRootLogger();
                String rootLevel = logger.getEffectiveLevel().toString();
                managerLogger.info("Level of the root logger is {}", rootLevel);
                return logger.getEffectiveLevel().toString();
            }
    
            /**
             * 设置日志的默认级别
             * 
             * @param newLevel
             */
            @ManagedAttribute(description = "set rootlogger's level")
            public void setRootLoggerLevel(String newLevel) {
                Logger logger = Logger.getRootLogger();
                if (!newLevel.equals(logger.getEffectiveLevel().toString())) {
                    logger.setLevel(Level.toLevel(newLevel));
                }
                managerLogger.info("设置日志默认级别为{}", newLevel);
            }
    
            /**
             * 获取指定工程的日志级别
             * 
             * @return
             */
            @ManagedAttribute(description = "get project log level")
            @ManagedOperationParameters({ @ManagedOperationParameter(name = "projectName", description = "project name") })
            public String getProjectLoggerLevel(String projectName) {
                return getLoggerLevel(projectName);
            }
    
            /**
             * 获取Logger的日志级别.
             */
            @ManagedOperation(description = "get appoint logger name ")
            @ManagedOperationParameters({ @ManagedOperationParameter(name = "loggerName", description = "Logger name") })
            public String getLoggerLevel(String loggerName) {
                Logger logger = Logger.getLogger(loggerName);
                return logger.getEffectiveLevel().toString();
            }
    
            /**
             * 设置logger级别
             * 
             * @param loggerName    日志名称
             * @param newLevel      新的等级
             */
            @ManagedAttribute
            @ManagedOperationParameters({
                    @ManagedOperationParameter(name = "loggerName", description = "logger name"),
                    @ManagedOperationParameter(name = "newLevel", description = "the level want to set") })
            public void setLoggerLevel(String loggerName, String newLevel) {
                Logger logger = Logger.getLogger(loggerName);
                logger.setLevel(Level.toLevel(newLevel));
            }
    
            /**
             * 设置工程logger级别
             * 
             * @param ProjectName   工程名
             * @param newLevel      新的等级
             */
            @ManagedAttribute
            @ManagedOperationParameters({
                    @ManagedOperationParameter(name = "projectName", description = "logger name"),
                    @ManagedOperationParameter(name = "newLevel", description = "the level want to set") })
            public void setProjectLevel(String ProjectName, String newLevel) {
                setLoggerLevel(ProjectName, newLevel);
            }
        }
    


    上面@ManagedResource表示指定该类的实例作为MBean注册到MBean Server中,然后可以通过对属性和方法分别使用@ManagedAttribute和@ManagedOperation来指定暴露的属性和方法。有关这些注解的详细内容,可以查阅相关文档。
        配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
        <description>JMX服务端配置</description>
        <!-- 配置MBean自动注册 -->
        <context:mbean-export default-domain="demo" registration="replaceExisting" />
        <!-- Log4j控制的 MBean -->
        <bean class="com.sinosoft.one.demo.jmx.Log4jManager">
            <property name="projectLoggerName" value="com.sinosoft.one.demo"/>
        </bean>
    </beans>
    


    三、使用HttpURLConnection进行JMX访问
    在web.xml中添加配置
    <servlet>
      <servlet-name>jolokia-agent</servlet-name>
      <servlet-class>org.jolokia.http.AgentServlet</servlet-class>
      <load-on-startup>3</load-on-startup>
    </servlet>
    <servlet-mapping>
      <servlet-name>jolokia-agent</servlet-name>
      <url-pattern>/jolokia/*</url-pattern>
    </servlet-mapping>
    

    HttpURLConnection访问的方法
    package com.sinosoft.one.demo.util;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.alibaba.fastjson.JSONObject;
    
    public class HttpConnectUtil {
        private Logger logger = LoggerFactory.getLogger(getClass());
        private String url;
        public String sendMessage() {
            url = "http://localhost:8080/demo/jolokia/";
            logger.info("=================开始获取信息,地址{}=================" , url);
            String responseMessage = "";
            StringBuffer resposne = new StringBuffer();
            HttpURLConnection httpConnection = null;
            DataOutputStream out = null;
            BufferedReader reader = null;
            try {
                URL urlPost = new URL(url);
                httpConnection = (HttpURLConnection) urlPost.openConnection();
                httpConnection.setDoOutput(true);
                httpConnection.setDoInput(true);
                // 参数长度太大,不能用get方式
                httpConnection.setRequestMethod("POST");
                // 不使用缓存
                httpConnection.setUseCaches(false);
                // URLConnection.setInstanceFollowRedirects是成员函数,仅作用于当前函数
                httpConnection.setInstanceFollowRedirects(true);
                // 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的
                // 意思是正文是urlencoded编码过的form参数
                httpConnection.setRequestProperty("Content-Type",
                        "application/x-www-form-urlencoded");
                // 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,
                // 要注意的是connection.getOutputStream会隐含的进行connect。
                // 实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。
                logger.debug("打开连接:" + url);
                httpConnection.connect();
                out = new DataOutputStream(httpConnection.getOutputStream());
                // The URL-encoded contend
                // 正文,正文内容其实跟get的URL中'?'后的参数字符串一致
                            List list=new ArrayList();
                list.add("com.sinosoft.one.demo");
                list.add("DEBUG");
                JSONObject jsonObject=new JSONObject();
                jsonObject.put("arguments", list);
                jsonObject.put("mbean", "log4j:logger=Log4j");
                jsonObject.put("type", "exec");
                jsonObject.put("operation", "setProjectLevel");
                System.out.println(jsonObject);
                // // DataOutputStream.writeBytes将字符串中的16位的unicode字符以8位的字符形式写道流里面
                out.writeBytes(jsonObject.toJSONString());
                // flush and close
                out.flush();
                reader = new BufferedReader(new InputStreamReader(
                        httpConnection.getInputStream()));
                while ((responseMessage = reader.readLine()) != null) {
                    resposne.append(responseMessage);
                }
                if (!"failure".equals(resposne.toString())) {
                    logger.info("success send to JMX");
                } else {
                    logger.debug("failure send to JMX");
                }
                // 将该url的配置信息缓存起来
                return resposne.toString();
            } catch (IOException e) {
                logger.error("连接失败,url={}" , url);
                return "failed";
            } finally {
                try {
                    if (null != out) {
                        out.close();
                    }
                    if (null != reader) {
                        reader.close();
                    }
                    if (null != httpConnection) {
                        httpConnection.disconnect();
                    }
                } catch (Exception e2) {
                    logger.error("http connection close error:{}", e2);
                }
            }
        }
    }
    


    使用httpUrlConnection来访问JMX需要用JSON格式定义一下参数
                      必选     类型及范围         说明
    mbean           true    string          MBean名称,取值为log:name=LogConfigs
    operation       true    string          操作方法名称,取值为addLogUrl
    arguments       false   array           参数列表,如果没有参数,可省略
    type            true    String          操作类型,取值为exec
    
    
    

    你可能感兴趣的:(spring)