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)。额,如果感觉有点懵,没关系,暂时理解自上而下分为三个层次就可以。
结构图如下:
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
-
标准 MBeans
-
MBean接口的类名称必须以 “MBean” 为后缀,如 MBean 定义为 “XXXMBean”,那么它的实现类名必须是 “XXX” -
MXBean接口的类名称必须以 “MXBean” 为后缀,或者接口标记 @javax.management.MXBean 注解
-
-
动态 MBeansjavax.management.DynamicMBean 接口
2.2 通知模型(Notification Model)
通知模型允许 MBean 广播管理事件,这种操作称之为通知。管理应用和其他对象注册成监听器。
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 }\r\n", 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 完全相同,测试如下:
此时的 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。
参考:
- 《JMX入门》:https://www.imooc.com/article/37008?block_id=tuijian_wz
- 《从动态日志到玩转JMX-视频》:http://www.365yg.com/a6529370724459610638/#mid=1561921308640258
- 《JMX通知》:http://www.tianshouzhi.com/api/tutorials/jmx/36
- 《JMX详解详细介绍及使用》:https://blog.csdn.net/update_java/article/details/79571237
- 《Spring JMX之三:通知的处理及监听》:https://www.cnblogs.com/duanxz/tag/JMX/
每天用心记录一点点。内容也许不重要,但习惯很重要!