美文网首页
JMX之介绍和mac环境使用介绍

JMX之介绍和mac环境使用介绍

作者: 爱编程的凯哥 | 来源:发表于2019-01-02 19:16 被阅读11次

    1.概念简介

    JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,
    实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。主要用于对JAVA应用程序和JVM进行监控和管理。

    JConsole和JVisualVM中能够监控到JAVA应用程序和JVM的相关信息都是通过JMX实现的。看下网上的一张结构图


    jmx

    三层结构,分别负责通信,代理,管理资源bean。

    • 最上的通信层:Agent如何被远端用户访问的细节。它定义了一系列用来访问Agent的接口和组件,包括Adapter和Connector的描述。

    • 中间的代理层:管理相应的资源,并且为远端用户提供访问的接口。Agent层构建在Intrumentation层之上,并且使用并管理 Instrumentation层内部描述的组件。Agent层主要定义了各种服务以及通信模型。该层的核心是一MBeanServer,所有的MBean都要向它注册,才能被管理。注册在MBeanServer上的MBean并不直接和远程应用程序进行通信,他们通过协议适配器(Adapter)和连接器(Connector)进行通信。通常Agent由一个MBeanServer和多个系统服务组成。JMX Agent并不关心它所管理的资源是什么

    • 基础管理bean:主要包括了一系列的接口定义和描述如何开发MBean的规范。通常JMX所管理的资源有一个或多个MBean组成,因此这个资源可以是任何由Java语言开发的组件,或是一个JavaWrapper包装的其他语言开发的资源。

    2. 使用举例

    很多开源框架都会用到jmx监控系统状态,比如我们熟悉的tomcat。下面举个简单的例子,说明我们实际使用场景。

    • 需求,做一个自定义的classloader类,进行加载我指定包路径下的类,并管理加载了这些类:

    • MBean定义
        根据Mbean的基础定义


      image.png

    我们使用最基本的standard MBean定义接口,此时有以下要求:

    1. 接口和实现类必须在同一包下
      2.接口名字为TestLoaderMBean,那么实现类必须叫TestLoader(MBean前字符串),一个单词不许错,否则会报错

    因此,我们接口定义

    package jmx.beans;
    
    public interface TestLoaderMBean {
    
        /**
         * 获取loader名
         * @return
         */
        String loaderName();
    
        /**
         * 获取loader总共加载的类数量
         * @return
         */
        int loaderClassSize();
    
        int getLoaderClassSize();
    
    
        /**
         * 获取加载了的所有类名称
         * @return
         */
        String loaderClassesNames();
    
        /**
         * 加载所有类
         */
        void loaderClasses();
    
        /**
         * 获取用户指定加载的包路径
         * @return
         */
        String packageName();
    
    
        /**
         * 销毁此加载器
         */
        void destory();
    }
    
    

    接口定义了loader的管理规范,声明了对应此loader的管理工作,然后定义其实现类

    package jmx.beans;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.TimeUnit;
    
    public class TestLoader implements TestLoaderMBean {
    
        private String name;
    
        private int loaderClassSize;
    
    
        private List<String> loaderClasss;
    
        private String packageName;
    
    
        public TestLoader(String name, String packageName) {
            this.name = name;
            this.packageName = packageName;
        }
    
        public TestLoader( ) {
        }
    
    
        @Override
        public String loaderName() {
            return name;
        }
    
        @Override
        public int getLoaderClassSize() {
            return loaderClassSize;
        }
    
        @Override
        public int loaderClassSize() {
            return loaderClassSize;
        }
    
        @Override
        public String loaderClassesNames() {
            StringBuffer sb=new StringBuffer();
            if(loaderClasss!=null){
                loaderClasss.stream().forEach(s->{
                    sb.append(s).append(";\n");
                });
            }
            return sb.toString();
    
        }
    
    
        @Override
        public String packageName() {
            return packageName;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    
        public void setLoaderClasss(List<String> loaderClasss) {
            this.loaderClasss = loaderClasss;
            if (loaderClasss != null) {
                loaderClassSize = loaderClasss.size();
            }
        }
    
    
        @Override
        public void loaderClasses() {
            System.out.println("加载" + packageName + "所有类");
            Random random = new Random(3);
            int i = random.nextInt(3);
            try {
                TimeUnit.SECONDS.sleep(i);
    
                String class1 = "com.class1";
                String class2 = "com.class2";
                String class3 = "com.class3";
                if (loaderClasss == null) {
                    loaderClasss = new ArrayList<>();
                }
                loaderClasss.add(class1);
                loaderClasss.add(class2);
                loaderClasss.add(class3);
                loaderClassSize = loaderClasss.size();
                System.out.println("size"+loaderClassSize);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void destory() {
            loaderClasss = null;
            loaderClassSize = 0;
            System.out.println("销毁所有类");
        }
    }
    
    

    模拟类中,注意到getLoaderClassSize()和loaderClassSize()方法是重复的,实际上这样是有区别的,一会我们会用到。
    最后需要注册一个代理服务

    package jmx;
    
    
    import jmx.beans.TestLoader;
    
    import javax.management.MBeanServer;
    import javax.management.ObjectName;
    import java.lang.management.ManagementFactory;
    
    public class LocalAgent {
    
    
        private MBeanServer mbs;
    
        public LocalAgent() throws Exception {
            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    
            ObjectName mbeanName = new ObjectName("jmx:type=testLoader");
    
            TestLoader mbean = new TestLoader("test","com.class");
    
            mbs.registerMBean(mbean, mbeanName);
    
            Thread.sleep(Long.MAX_VALUE);
    
    
        }
    
    
    
        public static void main(String args[]) throws Exception {
            LocalAgent agent = new LocalAgent();
        }
    
    }
    

    这样我们就是实现了简单jmx的管理示例,当然MBeanServer是支持rmi、html等远程监控协议,如rmi:

    package jmx;
    
    import jmx.beans.TestLoader;
    
    import javax.management.*;
    import javax.management.remote.JMXConnectorServer;
    import javax.management.remote.JMXConnectorServerFactory;
    import javax.management.remote.JMXServiceURL;
    import java.io.IOException;
    import java.lang.management.ManagementFactory;
    import java.rmi.registry.LocateRegistry;
    
    public class RmiAgent
    {
        public RmiAgent() {
    
            try {
                MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
    
                ObjectName mbeanName = new ObjectName("jmx:type=testLoader");
    
                TestLoader mbean = new TestLoader("test","com.class");
    
                mbs.registerMBean(mbean, mbeanName);
    
                int port=9000;
    
                //这个步骤很重要,注册一个端口,绑定url后用于客户端通过rmi方式连接JMXConnectorServer
                LocateRegistry.createRegistry(port);
                //URL路径的结尾可以随意指定,但如果需要用Jconsole来进行连接,则必须使用jmxrmi
                String urlStr="service:jmx:rmi:///jndi/rmi://localhost:"+port+"/jmxrmi";
                JMXServiceURL url = new JMXServiceURL(urlStr);
                JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
                jcs.start();
                System.out.println("rmi start: "+urlStr);
            } catch (MalformedObjectNameException e) {
                e.printStackTrace();
            } catch (InstanceAlreadyExistsException e) {
                e.printStackTrace();
            } catch (MBeanRegistrationException e) {
                e.printStackTrace();
            } catch (NotCompliantMBeanException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            RmiAgent rmiAgent=new RmiAgent();
        }
    }
    

    当然,我们实际使用的是tomcat等容器服务,所以说下远程tomcat开启jmx监控服务。其实,很简单,只需要,我们在自定义等setnev.sh中定义

    #开启服务
    JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote
    #指定ip
     -Djava.rmi.server.hostname=172.18.162.10  
    #指定端口
    -Dcom.sun.management.jmxremote.port=9999 
    #指定是否需要密码,如果为true,需要指定用户密码
    -Dcom.sun.management.jmxremote.authenticate=false 
    #是否需要ssl
     -Dcom.sun.management.jmxremote.ssl=false"
    
    

    此时再启动tomcat,就是自动启动tomcat等jmx了。

    对于客户端来说,我们有很多,常用的如JVisualVM、JConsole等。下面说下两种的使用方法。mac环境下,装好jdk后,这些都是已经集成好的(windows在java的bin目录下可以找到相应exe程序),打开终端输入JVisualVM就会自动启动JVisualVM客户端(对应JConsole一样的操作流程)


    mac操作

    JVisualVM功能是JConsole的升级版,包含了很多功能,可以查看内存、cpu、线程、生成堆快照.....等等。在JVisualVM使用JConsole ,安装JConsole插件即可,如下我安装了JConsole、MBean、Btrace等插件:


    安装插件

    插件安装后,我们可以就可以在JVisualVM中使用JConsole、MBean、Btrace了。

    1. 看下对远程tomcat对监控
      上面,我们已经在tomcat的bin目录下的setenv.sh文件中加入了启动jmx的脚本,我们已经可以通过客户端链接了,链接地址看下配置文件:
    #指定ip
     -Djava.rmi.server.hostname=172.18.162.10  
    #指定端口
    -Dcom.sun.management.jmxremote.port=9999
    

    端口号是9999,我们用JVisualVM链接


    链接远程tomcat

    这样我们就可以通过jmx来管理远程tomcat服务了,看下tomcat自带的一些jmx使用


    image.png

    类似这种管理类我们看到了很多,可以通过jmx添加用户,查看部署的app状态等等管理模块。

    1. 本地java服务监控
      在我们本地调试java代码时也可以使用,并且,可以使用Btrace进行代码注入。
      如上边我本地运行了一个LocalAgent的java程序


      本地监控

    对本地的java进程就可以做相应的监控操作了,并且我们安装了Btrace插件,可以通过Btrace脚本进行代码注入,如TestLoader类中有个loaderClasses方法,在不关服务的情况下,我想测一下此方法的执行时间。我们写个Btrace脚本

    /* BTrace Script Template */
    import com.sun.btrace.annotations.*;
    import static com.sun.btrace.BTraceUtils.*;
    
    @BTrace
    public class TracingScript {
        /* put your code here */
       @OnMethod(
         clazz="jmx.beans.TestLoader", 
         method="loaderClasses"
       )   
       public static void onWebserviceEntry(@ProbeClassName String pcn, @ProbeMethodName String pmn) {
           println(Strings.strcat(Strings.strcat(pcn, "."), pmn));
       }
    
       @OnMethod(
         clazz="jmx.beans.TestLoader", 
          method="loaderClasses",
         location=@Location(Kind.RETURN)
       )   
       public static void onWebserviceReturn(@ProbeClassName String pcn , @ProbeMethodName String pmn, @Duration long d) {
           print("leaving web service ");
           println(Strings.strcat(Strings.strcat(pcn, "."), pmn));
           println(Strings.strcat("Time taken (msec) ", Strings.str(d / 1000)));
           println("==========================");
       }
    }
    

    脚本中通过onWebserviceReturn方法的@Duration参数打印了方法的执行时间,这样就在我们无需关闭服务器的情况下,添加了代理代码。但要注意Btrace执行后,java类的class并不会还原,会一直存在,所以如果是生产环境要谨慎。特别@OnMethod通配符的写法,要修改所有匹配到的类。如果想了解Btrace更多内容,更多脚本,可以参考官方文档https://github.com/btraceio/btrace,里边有大部分场景的样例脚本:

    image.png

    好了,上边说了这么多,都是通过jmx延伸出,我们对于应用监控的一些方法。当然还有很多,如对于远程debug的jpda、对于内存分析的mat等等,有时间可以一起讨论下。jmx给我提供的是一种监控思想,如我们要实现自己的连接池、管理器等服务,可以借鉴相应的监控管理方法。

    相关文章

      网友评论

          本文标题:JMX之介绍和mac环境使用介绍

          本文链接:https://www.haomeiwen.com/subject/mkltrqtx.html