zoukankan      html  css  js  c++  java
  • Java JMX 监管

    Java JMX 监管

    JSR 规范系列目录(https://www.cnblogs.com/binarylei/p/10348178.html)

    JMX(Java Management Extensions) 技术提供构建分布式、Web、模块化的工具,以及管理和监控设备和应用的动态解决方案。从 Java 5 开始,JMX API 作为 Java 平台的一部分。

    一、整体架构

    JMX 的整体架构分为三层。设备层(Instrumentation Level)、代理层(Agent Level)、分发层(Distributed Level)。额,如果感觉有点懵,没关系,暂时理解自上而下分为三个层次就可以。

    结构图如下:

    JMX整体架构

    1.1 设备层

    "设备层"定义了信息模型。简单来说就是能被 java 描述出来的一个对象,这些对象成为“管理构件”,简称 MBean
    这些 MBean 就是我们要管理的指标,每个 MBean 都可以监控一类信息。

    MBean又分为以下几种:

    • Standard MBean(标准 MBeans) 设计和实现最为简单,Bean 的管理 通过接口方法来描述。MXBean 是一种特殊标准 MBean,它使用开放 MBean 的概念,允许通用管理,同时简化编码。
    • Dynamic MBean(动态 MBeans) 必须实现指定的接口,不过它在运行时能让管理接口发挥最大弹性。
    • Open MBean(开放 MBeans) 提供通用管理所依赖的基本数据类型以及用户友好的自描述信息。
    • Model MBean(模型 MBeans) 同样也是动态 MBean,在运行时能够完全可配置和自描述,为动态的设备资源提供带有默认行为的 MBean 泛型类。
    • MXBean

    这里以 Standard MBean、Dynamic MBean、MXBean 为例作为入门介绍

    (1) Standard MBean(标准 MBeans)

    这是最简单的 MBean,通过方法名来管理接口。Standard MBean 的实现依赖于一组特定的命名规范。

    规范如下:

    • 定义一个接口,名称为 xxxxMBean 的格式,必须以 MBean 结尾,以 User 为例,接口名为 UserMBean
      定义属性方法,假设 User 含有 id 和 name 属性,那么可以通过定义 getId、setId、getName 和 setName 来控制属性是否可读写,如果只定义了 set 方法,那么该属性可写不可读;如果只定义了 get 方法,那么该属性可读不可写;同时定义,那么就可读可写
    • 定义操作,其他非 get 和 set 方法,可以算是对该对象的操作
    • 接口定义完了,具体实现,需要继承上接口,以 UserMBean 为例,那么该实现也就必须命名为 User,然后实现该接口
    • 补充一点,参数和返回类型只能是简单的引用类型(如 String)和基本数据类型,其他类型编译不会出错,但是结果会稍有不同,稍后验证

    (2) Dynamic MBean(动态 MBeans)

    这种 MBean 就没有那么多限制,但是有一条硬性规则,必须实现 javax.management.DynamicMBean 接口。所有的属性都在运行时定义的。比较灵活

    (3) MXBean

    严格上讲,MXBean 这不是 MBean 的一种分类。MXBean 是 MBean 的一种,只是这货有些特殊,较为常用,所以放在一起讲。

    规范如下:

    • 实现 xxxMXBean 接口,或者不按照 MXBean 结尾来命名,但是需要加上 @MXBean 的注解
    • 支持各种数据类型,包括自定义类型
    • 其他方法命名规则与 MBean 类似

    1.2 代理层

    代理层就是用来管理资源的,管理 MBean。代理层的核心模块就是 MBean Server 和一系列附加的 MBean Service。
    而 MBean Server 其实就是 MBean 的容器,可以注册 Adapter、Connector、MBean 并且直接管理 MBean

    1.3 分发层

    这一层主要是根据不同的协议定义了对代理层进行各种操作的管理接口。

    二、Java JMX API

    2.1 MBeans

    1. 标准 MBeans

      • MBean 接口的类名称必须以 “MBean” 为后缀,如 MBean 定义为 “XXXMBean”,那么它的实现类名必须是 “XXX”
      • MXBean 接口的类名称必须以 “MXBean” 为后缀,或者接口标记 @javax.management.MXBean 注解
    2. 动态 MBeans javax.management.DynamicMBean 接口

    2.2 通知模型(Notification Model)

    通知模型允许 MBean 广播管理事件,这种操作称之为通知。管理应用和其他对象注册成监听器。

    Notification 类结构

    EventListener(监听器) 和 EventObject(事件源) 是 JDK 自带的事件监听规范。Spring 的 ApplicationListener 也实现了这套规范,只还过 Spring 是通过 context.pulishEvent 触发事件,这里是通过 NotificationBroadcaster#sendNotification 触发的。

    public void sendNotification(Notification notification) {
        if (notification == null) {
            return;
        }
    
        boolean enabled;
        for (ListenerInfo li : listenerList) {
            try {
                // 1. NotificationFilter 用于过滤事件类型
                enabled = li.filter == null || li.filter.isNotificationEnabled(notification);
            } catch (Exception e) {
                continue;
            }
                // 2. 触发事件
            if (enabled) {
                executor.execute(new SendNotifJob(notification, li));
            }
        }
    }
    

    2.3 MBean 元数据类(MetaData Class)

    元信息类包含描述所有MBean 管理接口的组件接口,其中包括:

    • 属性(Attribute) javax.management.MBeanAttributeInfo
    • 操作(Operation) javax.management.MBeanOperationInfo 和 javax.management.MBeanParameterInfo
    • 通知(Notification) javax.management.MBeanNotificationInfo
    • 构造器(Constructor) javax.management.MBeanConstructorInfo
    • javax.management.MBeanInfo

    2.4 代理相关(Agent)

    • MBean 服务器:javax.management.MBeanServer
    • 管理工厂:java.lang.management.ManagementFactory

    三、示例

    2.1 MBean

    (1) 定义接口 UserMBean

    // 定义 MBean
    public interface UserMBean {
    	// 属性
        void setId(Integer id);
        Integer getId();
    
        void setName(String name);
        String getName();
    
        void setBirthDate(Date date);
        Date getBirthDate();
    
        void setTime(LocalTime time);
        LocalTime getTime();
    
        void setTest(TestBean test);
        TestBean getTest();
    
        // 操作
        void printUserInfo();
        Date currentDate();
    }
    
    // TestBean 仅仅只是一个辅助测试的类
    public class TestBean {
        private String name;
        private int age;
        // 省略 get/set
    }
    

    (2) 定义实现类 User

    public class User implements UserMBean {
    
        private int id;
        private String name;
        private Date birthDate;
        private LocalTime time;
        private TestBean test;
        // 省略 get/set
    
        @Override
        public void printUserInfo() {
            System.out.printf("User: { id=%s, name=%s }
    ", this.id, this.name);
        }
    
        @Override
        public Date currentDate() {
            return new Date();
        }
    }
    

    (3) 注册 MBean,发布服务

    @Test
    public void test() throws Exception {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
    
        ObjectName name = new ObjectName("UserAgent:type=User1");
        User user = new User();
        user.setName("test");
        user.setId(1);
        user.setBirthDate(new Date());
        user.setTime(LocalTime.now());
        TestBean test = new TestBean();
        test.setName("mytest");
        test.setAge(11);
        user.setTest(test);
        mBeanServer.registerMBean(user, name);
    
        Thread.sleep(Long.MAX_VALUE);
    }
    

    此时可以通过 JDK 自带的 jconsole 或 jvisualvm 两个客户端进行连接,如下所示:

    可以看到 JDK 的八种基本类型和 String 类型可以查询和修改,而 Date 和 LocalTime 只能查询不能修改,自定义的 TestBean 即不能查询也不能修改。

    2.2 MXBean

    MXBean 接口定义有两种实现方式,一是以 MXBean 结尾,二是使用注解。

    public interface HelloMXBean {
        String getName();
        void setName(String name);
        void setTest(TestBean test);
        TestBean getTest();
    }
    
    // 注解方式可以不用以 MXBean 结尾
    @MXBean
    public interface Hello {
        String getName();
        void setName(String name);
        void setTest(TestBean test);
        TestBean getTest();
    }
    

    实现类和注册方式和 MBean 完全相同,测试如下:

    MXBean

    此时的 TestBean 可以正常查询。

    2.3 通知

    User 实现与通知相关的接口,如下:

    public class User extends NotificationBroadcasterSupport implements UserMBean,
            NotificationListener, NotificationFilter {
        private AtomicInteger sequenceNumber = new AtomicInteger();
        private String name;
    
        public User() {
            addNotificationListener(this, this, null);
        }
    
        // 修改属性值
        @Override
        public void setName(String name) {
            this.name = name;
            //发送通知
            Notification notification = new AttributeChangeNotification(this,
                    sequenceNumber.incrementAndGet(), System.currentTimeMillis(),
                    "name changed", "name", "String",
                    this.name, name);
            sendNotification(notification);
        }
    
        // 过滤
        @Override
        public boolean isNotificationEnabled(Notification notification) {
            if (notification instanceof AttributeChangeNotification) {
                AttributeChangeNotification attrNotification = (AttributeChangeNotification) notification;
                if ("name".equals(attrNotification.getAttributeName())) {
                    return true;
                }
            }
            return false;
        }
    
        // 执行监听方法
        @Override
        public void handleNotification(Notification notification, Object handback) {
            printUserInfo();
        }
    
        // 暴露通知信息给客户端,如 jconsole
        @Override
        public MBeanNotificationInfo[] getNotificationInfo() {
            String[] types = new String[]{AttributeChangeNotification.ATTRIBUTE_CHANGE};
            String name = AttributeChangeNotification.class.getName();
            String description = "An attribute of this MBean has changed";
            MBeanNotificationInfo info = new MBeanNotificationInfo(types, name,
                    description);
            return new MBeanNotificationInfo[]{info};
        }
    
        @Override
        public void printUserInfo() {
            System.out.println("name 的值修改为:" + this.name);
        }
    
        @Override
        public String getName() {
            return name;
        }
    }
    

    连接 jconsole 后修改 name 的属性值为 binarylei,控制台输出 name 的值修改为:binarylei

    参考:

    1. 《JMX入门》:https://www.imooc.com/article/37008?block_id=tuijian_wz
    2. 《从动态日志到玩转JMX-视频》:http://www.365yg.com/a6529370724459610638/#mid=1561921308640258
    3. 《JMX通知》:http://www.tianshouzhi.com/api/tutorials/jmx/36
    4. 《JMX详解详细介绍及使用》:https://blog.csdn.net/update_java/article/details/79571237
    5. 《Spring JMX之三:通知的处理及监听》:https://www.cnblogs.com/duanxz/tag/JMX/

    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    kthread_run
    05 七段数码管的实现
    Docker 面临的安全隐患,我们该如何应对
    7 天玩转 ASP.NET MVC — 第 6 天
    哪些问题困扰着我们?DevOps 使用建议
    如何使用 OneAPM 监控微软 Azure Cloud Service ?
    在 Java 中如何更高效地存储和管理 SQL 语句?
    规模化敏捷开发的10个最佳实践(上)
    如何在 Swift 语言下使用 iOS Charts API 制作漂亮图表?
    PHP 性能分析与实验——性能的宏观分析
  • 原文地址:https://www.cnblogs.com/binarylei/p/10360193.html
Copyright © 2011-2022 走看看