1. JMX介绍
JMX是Java Management Extensions的缩写,意为Java管理扩展,是一个标准也是一个框架,用来监控和管理JVM上面的资源,例如我们用jvisualvm监控远程的JVM时,远程需要开启JMX;
1.1 JMX架构
JMX作为一个标准和框架,要实现JMX编程必须基于其架构,架构就是与其相关的一些概念或架构分层;
JMX有一个MBean / 管理组件的概念,MBean是Management Bean的缩写,是对JVM上面所有资源的抽象,MBean作为一种Java对象,可以通过MBean的属性和方法访问JVM上面的各种资源;
JMX的架构由 分布层、代理层、设备层 组成;
设备层/MBean:将所有资源抽象成MBean组件,通过MBean访问资源,共有4类MBean,通常接触的都是标准MBean:
- 标准MBean:StdMBean接口描述行为,Std类实现行为; 要求:①接口和类必须在一个包下 ②接口名MBean结尾,实现类名为接口名前半部分;
- 动态MBean:实现DynamicMBean接口,接口中getMBeanInfo方法返回的MBeanInfo包含有哦此MBean暴露的所有属性(MBeanAttitudeInfo)和方法(MBeanOperationInfo),此MBeanInfo可以动态扩展;
- 开放MBean:忽略
- 模型MBean:忽略
- MXBean:MyMXBean接口描述行为,实现类实现行为且类名随意,要求:接口名MBean结尾或者接口用@MXBean注解;
代理层/MBeanServer:存放MBean组件的容器,提供注册和请求MBean的功能,容器就是管理组件服务器 / MBeanServer,创建管理组件服务器时需要制定服务器的域名,当向管理组件服务器注册管理组件时需要指定一个ObjectName对象来标识管理组件,ObjectName对象由域名和键值对列表构成,域名就是创建管理组件服务器的域名,键值对用来区分不同的MBean至少有一对且一般有name=XXX;
分布层:管理客户端如何访问管理组件服务器中的管理组件,有协议适配器或连接器两种实现形式,而适配器和连接器也作为一种MBean注册到管理组件服务器,常见的就是HTML适配器和RMI连接器,HTML适配器可以用浏览器客户端访问,RMI连接器可以用JConsole客户端访问;
1.2 JMX编程元素
编程元素就是如何按照JMX架构提供的规范来编写代码,JMX编程一般分为编写管理组件和编写管理组件服务器;
管理组件:包括设备层和分布层,分布层的适配器和连接器组件不需要自己编写,引入jar包即可,所以一般指编写自定义的管理组件,一般是标准MBean;
管理组件服务器:指代理层,分为三步,创建管理组件服务器,创建并注册自定义管理组件,创建并注册协议适配器或连接器组件;
2. JMX API
3. 实例
3.1 HTTP适配器
实例用标准MBean和Html适配器实现,MBean暴露了name属性,
Html适配器相关类不在jdk中,需要单独引入jar包;
自定义(标准)管理组件-接口:WorkerMBean
/**
* 管理组件接口
*/
public interface WorkerMBean {
String getName();
}
自定义(标准)管理组件-实现类:Worker
/**
* 管理组件实现类
*/
public class Worker implements WorkerMBean {
@Override
public String getName() {
return "kepus";
}
}
管理组件服务器:HtmlAdaptorAgent
import com.sun.jdmk.comm.HtmlAdaptorServer;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
/**
* 代理层/管理组件服务器:客户端通过http访问管理组件服务器
*/
public class HtmlAdaptorAgent {
// 域名
private static final String DOMAIN = "gc";
public static void main(String[] args) throws Exception {
//用指定域名创建管理组件服务器
MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer(DOMAIN);
// 创建自定义管理组件 -> 创建管理组件标识 -> 注册管理组件到管理组件服务器
Worker worker = new Worker();
//对象名用于标识不同MBean,由域名和键值对构成,键值对之间用逗号隔开
ObjectName objName4Worker = new ObjectName(DOMAIN + ":name=worker, creator=gongcong");
mBeanServer.registerMBean(worker, objName4Worker);
// 创建适配器管理组件 -> 创建管理组件标识 -> 注册适配器管理组件到管理组件服务器 -> 启动适配器
HtmlAdaptorServer htmlAdaptorServer = new HtmlAdaptorServer();
ObjectName objectName4Adaptor = new ObjectName(DOMAIN + ":name=htmlAdaptorServer, listenPort=8082");
mBeanServer.registerMBean(htmlAdaptorServer, objectName4Adaptor);
htmlAdaptorServer.start();
System.out.println("jmx started plz access in browser using http://localhost:8082");
}
}
运行结果:浏览器访问 http://localhost:8082/
3.2 RMI连接器
实例用MXBean和RMI连接器实现,MBean暴露了name属性,
自定义(MXBean)管理组件-接口:Worker
import javax.management.MXBean;
/**
* 管理组件接口
*/
@MXBean
public interface Worker {
String getName();
}
自定义(MXBean)管理组件-实现类:WorkerImpl
/**
* 管理组件实现类
*/
public class WorkerImpl implements Worker {
@Override
public String getName() {
return "angular";
}
}
管理组件服务器:RMIConnectorAgent
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.rmi.registry.LocateRegistry;
/**
* 代理层/管理组件服务器: 通过客户端通过rmi协议访问管理组件服务器
*/
public class RMIConnectorAgent {
// 域名
private static final String DOMAIN = "cg";
public static void main(String[] args) throws Exception {
//用指定域名创建管理组件服务器
MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer(DOMAIN);
// 创建自定义管理组件 -> 创建管理组件标识 -> 注册管理组件到管理组件服务器
WorkerImpl worker = new WorkerImpl();
ObjectName objectName4Person = new ObjectName(DOMAIN, "name", "worker");
mBeanServer.registerMBean(worker, objectName4Person);
//创建RMI注册表
LocateRegistry.createRegistry(41440);
/*
创建JMX管理组件服务器地址
service:jmx 为JMX URL前缀
:rmi://127.0.0.1:41441 表示rmi连接器;127.0.0.1为JMX管理组件服务器地址,可省略;41441为端口,可省略,省略是为随机
jndi/rmi://127.0.0.1:41440/cg 表示 管理组件服务器 在RMI注册表中的位置
*/
JMXServiceURL jmxServiceURL = new JMXServiceURL
("service:jmx:rmi://127.0.0.1:41441/jndi/rmi://127.0.0.1:41440/cg");
//创建RMI连接器组件并注册到管理组件服务器
JMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null,
mBeanServer);
// 启动连接器
jmxConnectorServer.start();
System.out.println("jmx started plz access " +
"use JConsole use service:jmx:rmi://127.0.0.1:41441/jndi/rmi://127.0.0.1:41440/cg");
}
}
运行结果:通过jdk自带的 jconsole.exe 访问
远程进程中的地址为:service:jmx:rmi://127.0.0.1:41441/jndi/rmi://127.0.0.1:41440/cg
3.3 HTML适配器+动态MBean
自定义动态管理组件:Worker
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ReflectionException;
import java.util.ArrayList;
import java.util.List;
/**
* 动态MBean,MBean暴露的属性和接口可以动态扩展
*/
public class Worker implements DynamicMBean {
// 暴露的属性列表
private List<String> lstExposedAttribute;
public Worker() {
this.lstExposedAttribute = new ArrayList<>();
// 默认暴露id 和 name
lstExposedAttribute.add("id");
lstExposedAttribute.add("name");
}
/**
* 获取此MBean暴露的属性和方法
*/
@Override
public MBeanInfo getMBeanInfo() {
// MBean类名
String className = this.getClass().getName();
// MBean的描述
String mbeanDescription = "this is dynamic mbean";
// 暴露的属性
MBeanAttributeInfo[] attributeInfos = new MBeanAttributeInfo[lstExposedAttribute.size()];
for (int i = 0; i < lstExposedAttribute.size(); i++) {
String attributeName = lstExposedAttribute.get(i);
attributeInfos[i] = new MBeanAttributeInfo(attributeName, "String", "exposed " +
"attribute " + attributeName, true, true, false);
}
// 暴露的方法:新增暴露属性
MBeanOperationInfo[] mBeanOperationInfos = new MBeanOperationInfo[1];
MBeanParameterInfo[] parameterInfos = new MBeanParameterInfo[]{new MBeanParameterInfo("attributeName",
"String", "new Attribute")};
mBeanOperationInfos[0] = new MBeanOperationInfo("addAttribute", "add new attribute", parameterInfos,
"void", MBeanOperationInfo.ACTION);
return new MBeanInfo(className, mbeanDescription, attributeInfos, null, mBeanOperationInfos,
null);
}
/**
* 暴露的属性的值都是通过此方法获取
*/
@Override
public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException,
ReflectionException {
return attribute + "'s value";
}
// 页面点击暴露的方法会调到此处
@Override
public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException,
ReflectionException {
if (actionName.equalsIgnoreCase("addAttribute")) {
this.lstExposedAttribute.add(((String) params[0]));
return "add " + params[0] + " success";
}
return null;
}
// 通过页面设置属性值:没实现,调用会抛出异常
@Override
public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException,
MBeanException, ReflectionException {
}
// 通过页面设置属性值:没实现,调用会抛出异常
@Override
public AttributeList setAttributes(AttributeList attributes) {
return null;
}
@Override
public AttributeList getAttributes(String[] attributes) {
return null;
}
}
管理组件服务器:HtmlAdaptorAgent
import com.sun.jdmk.comm.HtmlAdaptorServer;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
/**
* 代理层/管理组件服务器:客户端通过http访问管理组件服务器
*/
public class HtmlAdaptorAgent {
// 域名
private static final String DOMAIN = "gg";
public static void main(String[] args) throws Exception {
//用指定域名创建管理组件服务器
MBeanServer mBeanServer = MBeanServerFactory.createMBeanServer(DOMAIN);
// 创建自定义管理组件 -> 创建管理组件标识 -> 注册管理组件到管理组件服务器
Worker worker = new Worker();
//对象名用于标识不同MBean,由域名和键值对构成,键值对之间用逗号隔开
ObjectName objName4Worker = new ObjectName(DOMAIN + ":name=worker");
mBeanServer.registerMBean(worker, objName4Worker);
// 创建适配器管理组件 -> 创建管理组件标识 -> 注册适配器管理组件到管理组件服务器 -> 启动适配器
HtmlAdaptorServer htmlAdaptorServer = new HtmlAdaptorServer();
ObjectName objectName4Adaptor = new ObjectName(DOMAIN + ":name=htmlAdaptorServer, listenPort=8082");
mBeanServer.registerMBean(htmlAdaptorServer, objectName4Adaptor);
htmlAdaptorServer.start();
System.out.println("jmx started plz access in browser using http://localhost:8082");
}
}
运行结果:浏览器访问 http://localhost:8082/