美文网首页
java进阶之agent代理系列(二)——使用agentmain

java进阶之agent代理系列(二)——使用agentmain

作者: moutory | 来源:发表于2023-12-12 11:31 被阅读0次

    前言

    在上一篇文章中,我们掌握了使用premain模式来实现应用的代理。但premain模式存在着一个问题,需要程序启动的时候通过-javaagent来指定代理jar包位置,且一旦代理jar包启动失败,会导致主程序也启动失败。在jdk1.6之后官方推出了agentmain模式来让我们规避这个问题,本篇文章将针对这种agentmain模式来完成开发。

    本系列更多相关文章:

    java进阶之agent代理系列(一)——使用premain模式进行代理
    java进阶之agent代理系列(二)——使用agentmain模式进行代理
    java进阶之agent代理系列(三)——使用javassist实现接口耗时统计功能

    一、客户端代码开发

    这里我们简单写一个main方法即可,为了不让程序过快的结束掉,我们使用System.in.read();来让进程阻塞住。

    public class MainApplication {
    
        public static void main(String[] args) throws IOException {
            System.in.read();
        }
    }
    

    二、代理端代码开发

    步骤一:配置pom文件的maven插件

    这里的配置基本上和premain模式的一致,区别在于manifestEntries下面配置的是Agent-Class字段的值

    <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.1.0</version>
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
                            </manifest>
                            <manifestEntries>
                                <Agent-Class>com.qiqv.demo.JavaAgentTest</Agent-Class>
                                <!-- 为true时表示能够重新定义class -->
                                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                <!-- 为true时表示能够重新转换class,实现字节码替换 -->
                                <Can-Retransform-Classes>true</Can-Retransform-Classes>
                                <!-- 为true时表示能够设置native方法的前缀 -->
                                <Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
    
    步骤二:开发代理类入口

    这里我们不做复杂的逻辑,只要简单打印一下语句就行

    public class JavaAgentTest {
        public static void agentmain(String agentArgs, Instrumentation instrumentation) {
            System.out.println("agent main start");
            System.out.println("args:"+agentArgs);
        }
    }
    
    步骤三:将代理jar进行打包
    mvn clean package
    

    三、Attach工具开发

    在前两个章节中,我们开发好了客户端和代理端,那么现在我们不使用-javaagent参数直接启动客户端,显然代理端是不能直接对客户端进行代理的,此时我们需要有一个中介,让我们的客户端在启动后可以发现我们的代理。这个中介机制也被称为attach机制
    一般来说,我们会借助com.sun.tools.attach包下的VirtualMachine工具类,需要注意该类不是jvm标准规范,是由Sun公司自己实现的,使用前需要引入依赖:

    <dependency>
        <groupId>com.sun</groupId>
        <artifactId>tools</artifactId>
        <version>1.8</version>
        <scope>system</scope>
        <systemPath>${JAVA_HOME}\lib\tools.jar</systemPath>
    </dependency>
    

    PS:需要注意的是,如果使用的是jdk11或者以上的版本,那么默认情况下就已经有相关的依赖了,不需要单独再去引入

    下面是Attach工具的代码,内容十分的简单,我们可以通过jps命令来先手动查询到当前客户端的进程id,这里我查到的进程id是32448所以输入的值就是32448。其次执行loadAgent方法,方法的入参是我们agent的jar包存放路径。

    public class AttachTool {
    
        public static void main(String[] args) {
            try {
                VirtualMachine  vm= VirtualMachine.attach("32448");
                vm.loadAgent("C:\\Users\\98093\\Desktop\\class\\java-agent-mock.jar","param");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    参数填好后执行AttachTool的main方法,如果没有报错就说明agent已经成功挂载到主程序上面了,那我们可以返回去主程序的控制台上面观察,可以看到agent的agentmain方法已经正常执行了。

    客户端的控制台输出

    关于agentmain模式的流程图,我们可以从下面的图片来加深一下印象


    image.png

    小结

    事实上,arthas就是基于agentmain这个模式来进行开发的,已知需要监控的应用进程id,我们就可以通过代理来检测到当前应用的JVM运行情况。所以agentmain这种模式个人感觉也是一种相对实用的代理方法。

    相关文章

      网友评论

          本文标题:java进阶之agent代理系列(二)——使用agentmain

          本文链接:https://www.haomeiwen.com/subject/hpdvgdtx.html