前言
在上一篇文章中,我们掌握了使用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这种模式个人感觉也是一种相对实用的代理方法。
网友评论