如果大家仔细查看jconsole的时候会发现一个特殊的mbeans
他的下面几乎包含了所有的交互命令。
但是如果你点进去发现,需要填写参数的都是无法执行的。
jconsole目前确实没有这个能力,但是jvirtualvm装插件是可以执行的。
通过这个能力,我们就可以发现jmx似乎可以做其他命令做的所有事情。
只不过是需要特殊的插件。这里也可以说,我们通过这个mbeans是直接可以做你想做的操作的,而不是依赖命令行执行jstack等等。
下面我们就试试通过自己写code操作mbean来进行能力的获取。
demo
public static void main(String[] args) throws ReflectionException, MBeanException {
DiagnosticCommandMBean diagnosticCommandMBean = ManagementFactoryHelper.getDiagnosticCommandMBean();
Object threadPrint = diagnosticCommandMBean.invoke("threadPrint",
new Object[]{new String[]{}}, new String[]{String[].class.getName()});
System.out.println(threadPrint);
}
上面是一个打印stack的案例
我们先获取到DiagnosticCommandMBean。这里是用的jdk8。可以通过ManagementFactoryHelper来进行获取,这个类在高版本中去除了,我们得需要反射等方式来获取DiagnosticCommandMBean。
下面的threadPrint是通过jconsole查看命令的时候获取的。后面是需要跟着的参数。stack不需要参数,所以这里都是空对象。
注意:这里传递的是数组
因为很多命令。例如jfr start 他是有多个参数的,所以支持多个参数的设计,这里必须传递数据才可以正常运行。
jfr的特例
DiagnosticCommandMBean diagnosticCommandMBean = ManagementFactoryHelper.getDiagnosticCommandMBean();
Object threadPrint = diagnosticCommandMBean.invoke("jfrStart",
new Object[]{new String[]{"duration=60s","name=abc","filename=output.jfr"}}, new String[]{String[].class.getName()});
System.out.println(threadPrint);
上面的例子是一个多参数的例子,jfr的开启是可以跟着很多参数的。这里展示了多参数的启动方式。返回的数据都是命令行返回的结果。
jfr本身是一个常用的profiling手段,这里就是可以使用同进程的方式,触发jfr的数据dump一次可以获取到更丰富的数据。jfr的dump的地址,以及是否通过同进程采集,这些都是我们常用的方式。
对于返回值,这里描述的都是命令行的操作结果,命令行本身返回的都是string字符串,所以这里的返回结果一般都是字符串,我们都可以直接进行打印输出。
他的功能强大,可以利用jmc进行数据的查看。
功能的便利
jmx提供了貌似一个命令行提供的功能。他的优势是什么呢?
命令行的操作依赖socket通讯,socket的通讯文件在/tmp下。这个目录往往会因为linux的删除策略,删除掉socket文件,导致后面的功能都操作失效了。
很多线上系统有权限问题,无法直接登录机器操作,命令这些都有严格的执行权限要求,例如A用户启动的jvm程序,只有A用户才能操作,其他用户是操作不了的。所以导致了用户切换过于复杂,编写这样的系统非常的繁琐。
如何搭建一个控制系统
- 通过socket通讯
我们可以通过一个javaagent通过上面的mbean的代码控制,然后连接到一个服务端。这样相比jmx的好处就是不用担心实现预定的端口,如果是以前jmx的模式,需要先确定端口,然后客户端再连接上去。这样的问题是端口需要事先约定,要和临时的端口起冲突,需要做预留端口,否则socket冲突启动不起来。
在socket的基础上。可以远程获取数据信息,我们常见的apm系统就是这样,通过jmx,socket把信心获取走,但是原来的mbean不太多,功能没有这么复杂,导致了很多功能受限,例如要依赖tools.jar去做jmap等操作,现在就可以减少这样的依赖,直接通过mbean就可以。 - 同进程操作更方便
这样我们就可以通过jmx来描述一个控制系统,是针对进程级别的,因为是同进程,所以只要标志出是我们找的进程就可以,不用再过多的考虑权限的问题,同进程有最大的权限。 - 支持分布式任务
也不用考虑分布式任务的问题,javaagent自动注册过来,我们不用纠结任务会跑在哪个机器上,只要看到多余的机器就是我们的新启动的进程。 - 减少数据的压力
如果数据都在本地存储,本身是一个影响性能的事情, 通过socket可以把数据存储到远端,达到在不丢失数据的基础上还能尽量的保存更多的数据。