arthas命令实现原理-MBean的使用

发布时间 2024-01-11 15:24:59作者: 茄子_2008

MBean,MXBean简介
MBean是一个托管的java bean对象,MBean是一个托管Java对象,类似于JavaBeans组件,遵循JMX(Java Management Extensions,即Java管理扩展)规范中规定的设计模式。MBean可以表示任何需要管理的资源。MBeans 公开了一个管理接口,该接口由以下部分组成:

一组可读或可写属性,或两者兼而有之
一组可调用的操作
自我描述
JMX 规范中定义了如下五种类型的MBean:

Standard MBeans
Dynamic MBeans
Open MBeans
Model MBeans
MXBean
本文主要描述standard MBeans以及MXBeans类。

Stadard Mbeans
标准的MBean就是定义一个java 接口,该接口必须以MBean结尾,同时定义定义一个接口的实现类,实现类的名称是接口名称去掉MBean后的名字。 每一个定义在接口中的方法代表一个MBean的属性或者操作。MBean通过公共方法以及遵从特定的设计模式封装了属性和操作,以便暴露给管理应用程序。例如,一个只读属性在管理构件中只有Get方法,既有Get又有Set方法表示是一个可读写的属性。

定义HelloMBean接口,该接口必须以MBean结尾

package com.allen.mbean;
public interface HelloMBean {
public void sayHello();
public int add(int x, int y);
public String getName();
public int getCacheSize();
public void setCacheSize(int size);
}
定义MBean接口的实现类,当前类的名称为接口名称去掉结尾的MBean剩余的字符

package com.allen.mbean;
public class Hello implements HelloMBean {
public void sayHello() {
System.out.println("hello, world");
}
public int add(int x, int y) {
return x + y;
}
public String getName() {
return this.name;
}
public int getCacheSize() {
return this.cacheSize;
}
public synchronized void setCacheSize(int size) {
this.cacheSize = size;
System.out.println("Cache size now " + this.cacheSize);
}
private final String name = "Reginald";
private int cacheSize = DEFAULT_CACHE_SIZE;
private static final int
DEFAULT_CACHE_SIZE = 200;
}

创建测试MBean功能的类, 当前类启动后在MBeanServer中注册Hello这个对象。 启动参数中增加 -Djava.rmi.server.hostname=localhost 当时我的mac电脑启动程序每添加这个参数导致jconsole连不上。

package com.allen.mbean;
import javax.management.*;
import java.lang.management.ManagementFactory;
public class HelloMBeanTest {
public static void main(String[] args) throws InterruptedException, MalformedObjectNameException, NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=Hello");
Hello mbean = new Hello();
mbs.registerMBean(mbean, name);
Thread.sleep(Long.MAX_VALUE);
}
}
使用jconsole连到当前jvm进程在MBean的tab页下可以看到有CacheSize,Name属性, sayHello,add操作

 

其实还可以通过jmx 连接到当前jvm,使用jmx远程操作当前Mbean,代码如下,其中FindUrlByPid 是本地通过pid查找到对应进程jmx的url串, 可通过获取对应的源码

package com.allen.mbean;
import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
public class ClientDemo {
// 执行参数中注意增加上HelloMBeanTest运行的pid
public static void main(String[] args) throws IOException, MalformedObjectNameException, InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
String url = FindUrlByPid.main(args);
if (url == null) {
System.out.println("can not find the jmx url by pid");
return;
}
JMXServiceURL jmxServiceURL = new JMXServiceURL(url);
JMXConnector connect = JMXConnectorFactory.connect(jmxServiceURL, null);
MBeanServerConnection mBeanServerConnection = connect.getMBeanServerConnection();
ObjectInstance objectInstance = mBeanServerConnection.getObjectInstance(new ObjectName("com.example:type=Hello"));
System.out.println(mBeanServerConnection.getAttribute(new ObjectName("com.example:type=Hello"), "Name"));
mBeanServerConnection.invoke(new ObjectName("com.example:type=Hello"), "sayHello", new Object[]{}, new String[]{});
}
}

MXBeans
MXBean是一种特殊的MBean,不仅特殊在名字不一样,主要是在于在接口中会引用到一些其他类型的类时,其表现方式的不一样。在MXBean中,如果一个MXBean的接口定义了一个属性是一个自定义类型,如果MXBean定义了一种自定义的类型,当JMX使用这个MXBean时,这个自定义类型就会被转换成一种标准的类型,这些类型被称为开放类型,是定义在javax.management.openmbean包中的。而这个转换的规则是,如果是原生类型,如int或者是String,则不会有变化,但如果是其他自定义类型,则被转换成CompositeDataSupport类,这样,JMX调用这个MXBean提供的接口的时候,classpath下没有这个自定义类型也是可以调用成功的,但是换做MBean,则调用方的classpath下必须存在这个自定义类型的类定义。

在标准Mbean的基础上增加了User类型的属性,jconsole查看属性时会显示不可用

 

将MBean更换为MXBean后,通过jconsole查看属性时可以正常查看到

 

arthas使用的MXBean
arthas中部分系统状态信息的命令的实现是通过MXBean的调用返回对应的数据,下表是我现阶段的统计的命令的以及当前命令使用到的MXBean

命令

MXBean

描述

vmoption

HotSpotDiagnosticMXBean

通过该命令可以查看或者修改jvm 诊断性配置参数,如 PrintGC, PrintGCDetails等参数

thread

ThreadMXBean

通过该命令我们可以查看线程线程信息

jvm

RuntimeMXBean

ClassLoadingMXBean

CompilationMXBean

GarbageCollectorMXBean

MemoryManagerMXBean

MemoryMXBean

OperatingSystemMXBean

当前命令会展示jvm的runtime, CLASS-LOADING,COMPILATION等一系列状态信息

dashboard

聚合几种mxbean进行信息的获取

仪表盘的作用主要是展示thread, memory, gc, vm信息,也是通过jvm命令中的个别MXBean实现的

arthas-jvm命令实现流程
从前面章节可以看出arthas服务端时怎么处理请求, 当我们在客户端执行jvm命令之后,到arthas内部后由JvmCommand类进行处理,核心方法为:

@Override
public void process(CommandProcess process) {
JvmModel jvmModel = new JvmModel();
addRuntimeInfo(jvmModel);
addClassLoading(jvmModel);
addCompilation(jvmModel);
if (!garbageCollectorMXBeans.isEmpty()) {
addGarbageCollectors(jvmModel);
}
if (!memoryManagerMXBeans.isEmpty()) {
addMemoryManagers(jvmModel);
}
addMemory(jvmModel);
addOperatingSystem(jvmModel);
addThread(jvmModel);
addFileDescriptor(jvmModel);
process.appendResult(jvmModel);
process.end();
}

从上面的代码中,我们可以看出,当前方法核心流程就时创建一个JvmModel对象,向该对象中添加各类信息,运行信息,类加载信息,gc信息,内存占用,线程等各类信息,接下来我们就几种信息获取进行具体的分析。着重介绍Runtime信息,Classloading信息,Thread信息的获取,其它方面的信息与这三种实现方式大体一致。

Runtime信息
//RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
String bootClassPath = "";
try {
bootClassPath = runtimeMXBean.getBootClassPath();
} catch (Exception e) {
// under jdk9 will throw UnsupportedOperationException, ignore
}
String group = "RUNTIME";
jvmModel.addItem(group,"MACHINE-NAME", runtimeMXBean.getName());
jvmModel.addItem(group, "JVM-START-TIME", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(runtimeMXBean.getStartTime())));
jvmModel.addItem(group, "MANAGEMENT-SPEC-VERSION", runtimeMXBean.getManagementSpecVersion());
jvmModel.addItem(group, "SPEC-NAME", runtimeMXBean.getSpecName());
jvmModel.addItem(group, "SPEC-VENDOR", runtimeMXBean.getSpecVendor());
jvmModel.addItem(group, "SPEC-VERSION", runtimeMXBean.getSpecVersion());
jvmModel.addItem(group, "VM-NAME", runtimeMXBean.getVmName());
jvmModel.addItem(group, "VM-VENDOR", runtimeMXBean.getVmVendor());
jvmModel.addItem(group, "VM-VERSION", runtimeMXBean.getVmVersion());
jvmModel.addItem(group, "INPUT-ARGUMENTS", runtimeMXBean.getInputArguments());
jvmModel.addItem(group, "CLASS-PATH", runtimeMXBean.getClassPath());
jvmModel.addItem(group, "BOOT-CLASS-PATH", bootClassPath);
jvmModel.addItem(group, "LIBRARY-PATH", runtimeMXBean.getLibraryPath());

从上面的代码中,可以清晰的看出来Runtime信息都是通过RuntimeMXBean对象获取的,通过该对象我们可以获取系统运行时的一些信息。通过这个MXBean我们可以获取到很多有用的信息:

通过getName信息,可以获取到当前进程的id, getName获取的数据的格式时pid@hostname
通过getStartTime,可以获取到jvm启动的时间
通过getInputArguments,可以获取到jvm启动时间
。。。 不一一举例
Classloading信息
ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean();
private void addClassLoading(JvmModel jvmModel) {
String group = "CLASS-LOADING";
jvmModel.addItem(group, "LOADED-CLASS-COUNT", classLoadingMXBean.getLoadedClassCount());
jvmModel.addItem(group, "TOTAL-LOADED-CLASS-COUNT", classLoadingMXBean.getTotalLoadedClassCount());
jvmModel.addItem(group, "UNLOADED-CLASS-COUNT", classLoadingMXBean.getUnloadedClassCount());
jvmModel.addItem(group, "IS-VERBOSE", classLoadingMXBean.isVerbose());
}
通过ClassLoadingMXBean可以获取到jvm加载到类个数,卸载到类个数,总共加载到类个数,是否为类加载系统启用verbose输出,其实该MXBean还有一个setVerbose方法,用于动态设置类加载的verbose属性

Thread信息
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
private void addThread(JvmModel jvmModel) {
String group = "THREAD";
jvmModel.addItem(group, "COUNT", threadMXBean.getThreadCount())
.addItem(group, "DAEMON-COUNT", threadMXBean.getDaemonThreadCount())
.addItem(group, "PEAK-COUNT", threadMXBean.getPeakThreadCount())
.addItem(group, "STARTED-COUNT", threadMXBean.getTotalStartedThreadCount())
.addItem(group, "DEADLOCK-COUNT",getDeadlockedThreadsCount(threadMXBean));
}
核心是通过ThreadMXBean进行数据的获取, 在arthas,jvm命令中使用ThreadMXBean相对简单,仅仅获取的线程数,守护线程数,peak状态的线程数,启动过的线程数,死锁线程数。

dashboard命令同样会展示cpu占用率比较高的10个线程, 其中还使用到了getThreadCpuTime的方法,通过该方法可以获取的线程的的cpu时间

arthas用到这些MXBean的作用
java APM(Application Performance Monitor 应用性能监控)程序,中对与jvm的信息采集中,使用到很多MXBean,如我之前研究的的pinpoint实现中,对于应用性能采集:

activethread, buffer, cpu, deadlock, filedscriptor, gc, loadedclass, memory, totalthread这些指标跟arthas的实现获取方式几乎一致,都是通过mxbean获取的。

 

后续如果有项目需求,采集系统性能,个人觉得完全可以通过这样的jdk提供的MXBean进行获取
————————————————
版权声明:本文为CSDN博主「丁Allen」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lian_forever/article/details/118858587