动态追踪技术是一个可以不用重启线上java项目来进行问题排查的技术,也叫Java Agent技术,可以利用它来构建一个附加的代理程序,用来协助检测性能,还可以替换一些现有功能,甚至JDK的一些类我们也能修改,有点像JVM级别的AOP功能。
既然作为JVM的AOP,就必须要有AOP的功能,所以Java Agent提供了两个类似于AOP的方法:
已有一个项目案例agent-demo:
package com.morris.demo;
public class MainRun {
public static void main(String[] args) throws InterruptedException {
while (true) {
new Account().hello();
Thread.sleep(1000);
}
}
}
package com.morris.demo;
public class Account {
public void hello() {
System.out.println("hello world");
}
}
要构建一个agent程序,大体可分为以下步骤:
Java Agent体现方式是一个jar包,这里为了简单起见,将agent和项目放在同一个工程中。
加入maven依赖,我们借用javassist完成字节码增强:
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.24.1-GA</version>
</dependency>
创建一个普通的Java类,添加premain或者agentmain方法,它们的参数完全一样,示例中使用的是AgentApp。
package com.morris.agent;
import com.morris.demo.Account;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
public class AgentApp {
//在main执行之前的修改
public static void premain(String agentOps, Instrumentation inst) {
System.out.println("==============enter premain==============");
inst.addTransformer(new Agent());
}
//控制类运行时的行为
public static void agentmain(String agentOps, Instrumentation inst) throws UnmodifiableClassException {
System.out.println("==============enter agentmain==============");
inst.addTransformer(new Agent());
inst.retransformClasses(Account.class);
}
}
假如我们要统计某个方法的执行时间,使用JavaAssist工具来增强字节码。
例如要增强demo项目中MainRun类的hello方法,那么编写一个Agent类实现ClassFileTransformer接口,然后在transform方法中实现以下逻辑:
package com.morris.agent;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class Agent implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.endsWith("Account")) {
try {
String loadName = className.replaceAll("/", ".");
CtClass ctClass = ClassPool.getDefault().get(loadName);
CtMethod ctMethod = ctClass.getDeclaredMethod("hello");
ctMethod.addLocalVariable("_begin", CtClass.longType);
ctMethod.insertBefore("_begin = System.nanoTime();");
ctMethod.insertAfter("System.out.println(System.nanoTime() - _begin);");
return ctClass.toBytecode();
} catch (Throwable e) {
e.printStackTrace();
}
}
return classfileBuffer;
}
}
在src/main/resources/META-INF/目录下新建MANIFEST.MF文件,内容如下:
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-class: com.morris.agent.AgentApp
Agent-class: com.morris.agent.AgentApp
由于maven打包过程中会自己创建MANIFEST.MF,所有需要指定maven使用我们MANIFEST.MF文件:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
最后使用mvn package
命令进行打包,得到agent-1.0-SNAPSHOT.jar
包。
直接命令行中加入-javaagent参数即可:
java -javaagent:C:\development\project\agent-demo\target\agent-demo-1.0-SNAPSHOT.jar -classpath C:\development\project\agent-demo\demo\target\classes;C:\Users\user\.m2\repository\org\javassist\javassist\3.24.1-GA\javassist-3.24.1-GA.jar com.morris.demo.MainRun
运行结果如下:
==============enter premain==============
hello world
157632
执行后,直接输出hello world。通过增强以后,还额外的输出了执行时间,以及一些debug信息。其中,debug信息在main方法执行之前输出。
Agentmain这种模式一般用在一些诊断工具上,使用jdk/lib/tools.jar中的工具类中的Attach API,可以动态的为运行中的程序加入一些功能。它的主要操作步骤如下:
package com.morris.demo;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import java.util.List;
import java.util.Properties;
public class JvmAttach {
public static void main(String[] args)
throws Exception {
List<VirtualMachineDescriptor> list = VirtualMachine.list();
VirtualMachine virtualMachine = null;
for (VirtualMachineDescriptor vmd : list) {
if (vmd.displayName().endsWith("MainRun")) {
virtualMachine = VirtualMachine.attach(vmd.id());
virtualMachine.loadAgent("C:\\development\\project\\agent-demo\\target\\agent-demo-1.0-SNAPSHOT.jar", "agent");
break;
}
}
System.in.read();
virtualMachine.detach();
}
}
注意:写这段代码的时候IDE可能提示找不到jar包,这时候将jdk/lib/tools.jar添加的项目的classpath中。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://morris131.blog.csdn.net/article/details/107941033
内容来源于网络,如有侵权,请联系作者删除!