自研Java虚拟机监控工具——Jvmm

/ JVM / 0 条评论 / 903浏览

你是不是想在程序运行时获取虚拟机信息?甚至采样获取火焰图?用jdk自带的jstat、jps使用总感觉不方便而且不太好自定义,使用Arthas功能倒是很全但是太重了,可能用不到那么多,而且也没办法在自己代码中调用这些接口,今天推荐一个工具:Jvmm

github地址:https://github.com/tzfun/jvmm

gitee地址:https://gitee.com/tzfun/jvmm

这个工具提供了三种方式使用:API调用Server服务命令行客户端

一、API方式

先说API调用吧,想要在自己的程序中使用它只需要引入core包

<dependency>
  <groupId>io.github.tzfun.jvmm</groupId>
  <artifactId>jvmm-core</artifactId>
  <version>1.2.0</version>
</dependency>

然后在自己的代码中就可以直接获取内存、CPU负载、线程栈信息等,甚至你可以用他的JvmmProfiler直接生成出火焰图

public class ApiCallDemo {
    public static void main(String[] args) {
        //  The jvmm collector can obtain the following information:
        //  Operating system: basic information, Memory, CPU, Process information
        //  JVM: Memory, GC, Class, Thread, Compilation information
        JvmmCollector collector = JvmmFactory.getCollector();

        MemoryInfo memory = collector.getMemory();
        List<MemoryManagerInfo> memoryManager = collector.getMemoryManager();
        List<MemoryPoolInfo> memoryPool = collector.getMemoryPool();
        SystemStaticInfo systemStatic = collector.getSystemStatic();
        SystemDynamicInfo systemDynamic = collector.getSystemDynamic();
        ClassLoadingInfo classLoading = collector.getClassLoading();
        List<GarbageCollectorInfo> garbageCollector = collector.getGarbageCollector();
        CompilationInfo compilation = collector.getCompilation();
        ProcessInfo process = collector.getProcess();
        ThreadDynamicInfo threadDynamic = collector.getThreadDynamic();
        String[] threadsInfo = collector.dumpAllThreads();

        //  The jvmm executor
        JvmmExecutor executor = JvmmFactory.getExecutor();

        executor.gc();
        executor.setMemoryVerbose(true);
        executor.setClassLoadingVerbose(true);
        executor.setThreadContentionMonitoringEnabled(true);
        executor.setThreadCpuTimeEnabled(true);
        executor.resetPeakThreadCount();

        //  The jvmm profiler, only supports MacOS and Linux environments
        JvmmProfiler profiler = JvmmFactory.getProfiler();
        File file = new File("jvmm_test.html");
        //  Sampling cpu information, duration 10 seconds, output with html report
        Future<String> future = JvmmFactory.getProfiler().sample(file, ProfilerEvent.cpu, ProfilerCounter.samples, 10, TimeUnit.SECONDS);

        try {
            //  The waiting time is recommended to be longer than the sampling time
            future.get(12, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException e) {
            e.printStackTrace();
        }
    }
}

使用Profiler就可以生成这样的火焰图了,不用再自己去安装其他环境balabala什么的,不过要注意的是这个功能只支持MacLinux,其他功能基本上都支持

火焰图

二、Server服务方式

这个工具还提供了以服务的方式运行监控器,相当于在你的服务里面再启动了一个微型服务,这个微型服务主要用于虚拟机监控的,话不多说上代码。

要使用Server服务只需要引用他提供的server依赖

<dependency>
  <groupId>io.github.tzfun.jvmm</groupId>
  <artifactId>jvmm-server</artifactId>
  <version>1.2.0</version>
</dependency>

启动代码

import org.beifengtz.jvmm.core.conf.Configuration;
import org.beifengtz.jvmm.server.ServerBootstrap;

public class ServerBootDemo {
    public static void main(String[] args) throws Throwable {
        Configuration config = Configuration.newBuilder()
                .setName("jvmm_server_test")
                .setPort(5010)
                .setAutoIncrease(true)
                .setHttpMaxChunkSize(52428800)
                .setLogLevel("info")
                .setLogUseJvmm(true)
                .setSecurityEnable(true)
                .setSecurityAccount("jvmm_acc")
                .setSecurityPassword("jvmm_pwd")
                .setWorkThread(2)
                .build();
        ServerBootstrap server = ServerBootstrap.getInstance(config);
        server.start();

        Thread.sleep(3000);

        server.stop();
    }
}

Configuration配置好相关的参数然后调用ServerBootstrapstart()方法就可以了,使用起来还是蛮简单的,同时他还支持yml文件配置,具体使用方法去github或者gitee看吧。

但稍微不好的一点是他这个只能通过他提供的工具才能和server通信,可能是为了安全性考虑吧,他的实现并不是http协议,而是特有的一种socket协议,不管怎样官方提供了通信工具就行。

如果你只是想和server通信的话没必要引入整个server依赖,只需要引入他提供的通信包就可以了

<dependency>
  <groupId>io.github.tzfun.jvmm</groupId>
  <artifactId>jvmm-convey</artifactId>
  <version>1.2.0</version>
</dependency>

要和server通信就必须使用他提供的JvmmConnector,直接看代码吧

public class ServerConveyDemo {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup executor = JvmmChannelInitializer.newEventLoopGroup(1);
        JvmmConnector connector = JvmmConnector.newInstance("127.0.0.1", 5010, false, "jvmm_acc", "jvmm_pwd", executor);
        Future<Boolean> f1 = connector.connect();
        if (f1.await(3, TimeUnit.SECONDS)) {
            if (f1.getNow()) {

                connector.registerCloseListener(() -> {
                    System.out.println("Jvmm connector closed");
                    executor.shutdownGracefully();
                });

                connector.registerListener(response -> {
                    if (Objects.equals(response.getType(), GlobalType.JVMM_TYPE_PONG.name())) {
                        System.out.println("pong");
                        connector.close();
                    }
                });

                connector.send(JvmmRequest.create().setType(GlobalType.JVMM_TYPE_PING));
            } else {
                System.err.println("Authentication failed!");
            }
        } else {
            System.err.println("Connect time out");
        }
    }
}

三、命令行方式

Jvmm提供了命令行方式可以让你动态的将server安装到你的程序上,并且与server建立联系,如果你不想自己写代码看他的API,这个方法再合适不过了,API里面的方法他已经帮你在client工具里都实现了。

首先你需要去下载他的包

github下载地址:https://github.com/tzfun/jvmm/tags

gitee下载地址:https://gitee.com/tzfun/jvmm/tags

下载下来之后会解压,首先你需要修改config.yml配置文件

name: jvmm_server
port:
  bind: 5011
  #  遇到冲突时监听端口自动自增
  autoIncrease: false

http:
  maxChunkSize: 52428800

security:
  enable: false
  account: jvmm_test_acc
  password: jvmm_test_pwd

log:
  level: debug
  useJvmm: true

workThread: 1

然后把server载入到你的程序中,执行命令

attach.bat -p 8080

这里的 -p 是指你自己的程序运行的端口,如果你的程序没有监听端口,那么就需要知道它运行的进程号,然后使用下面方式载入

attach.bat -pid 15200

载入之后你的程序里应该就会打出这样的日志

[Jvmm] [Info ] Try to start jvmm server service. target port: 5011
[Jvmm] [Info ] Jvmm server service started on 5011, node name: jvmm_server

然后再使用client去连接server,执行命令

java -jar jvmm-client.jar -h 127.0.0.1:5011

# 连接成功应该会有以下输出
[Jvmm] [Info ] Start to connect jvmm agent server...
[Jvmm] [Info ] Connect successful! You can use the 'help' command to learn how to use. Enter 'exit' to safely exit the connection.

之后你就进入了client模式了,比如我想获取系统数据,使用info命令就可以了

> info -t system
{
   "name":"Windows 10",
   "version":"10.0",
   "arch":"amd64",
   "loadAverage":-1.0,
   "availableProcessors":6,
   "timeZone":"Asia/Shanghai"
}
> info -t systemDynamic
{
   "committedVirtualMemorySize":927412224,
   "freePhysicalMemorySize":4898332672,
   "freeSwapSpaceSize":5039652864,
   "processCpuLoad":0.15359669391103387,
   "processCpuTime":8171875000,
   "systemCpuLoad":0.2651394929702138,
   "totalPhysicalMemorySize":0,
   "totalSwapSpaceSize":28508299264
}

要生成火焰图,因为我这用的是windows没法生成,大家可以自行到Linux或者MacOs试一试

> profiler -f test.html

如果你想关闭server就用shutdown命令,这个命令并不会关掉你的程序,只是关掉运行在你程序里面的jvmm server程序

> shutdown

[Jvmm] [Info ] Connected channel inactive, trigger to close connector...

微信公众号浏览体验更佳,在这里还有更多优秀文章为你奉上,快来关注吧!

北风IT之路