美文网首页
JavaAgent探针技术第二篇:在主程序运行之后的代理程序

JavaAgent探针技术第二篇:在主程序运行之后的代理程序

作者: GeekerLou | 来源:发表于2020-02-04 17:08 被阅读0次

前言

在主程序运行之前的agent模式有一些缺陷,例如需要在主程序运行前就指定javaagent参数,premain方法中代码出现异常会导致主程序启动失败等,为了解决这些问题,JDK1.6以后提供了在程序运行之后改变程序的能力。本文将重点介绍如何在程序运行中植入agent的方法。

案例

  1. 在AgentDemo工程中新增一个agentDemo2的类:
    由于是在主程序运行后再执行,意味着我们可以获取主程序运行时的信息,这里我们打印出来主程序中加载的类名。
import java.lang.instrument.Instrumentation;

/**
 * @Description
 * @Author louxiujun
 * @Date 2020/2/4 15:12
 **/
public class AgentDemo2 {
    public static void agentmain(String agentArgs, Instrumentation inst) {
        System.out.println("load agent after main run. args=" + agentArgs);

        Class<?>[] classes = inst.getAllLoadedClasses();
        for (Class<?> cls : classes) {
            System.out.println(cls.getName());
        }

        System.out.println("agent run completely");
    }
}
  1. 添加Agent-Class参数,打成Jar包
    pom文件build修改为如下内容,关键在于指定<Agent-Class>标签值为AgentDemo2的完整路径,可以通过IDEA提供的copy reference复制过来。
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.3.1</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
            </manifest>
            <manifestEntries>
                <!--方式1:在主程序运行之前的代理程序-->
                <!--<Premain-Class>com.alibaba.ei.agent.AgentDemo</Premain-Class>-->

                <!--方式2:在主程序运行之后的代理程序-->
                <Agent-Class>com.alibaba.ei.agent.AgentDemo2</Agent-Class>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

最后通过下面的指令,将新编写的agent包安装到本地maven仓库之中:

mvn clean install -Dmaven.test.skip=true
  1. 添加pom依赖,清空JVM启动参数
    回到AgentTest工程,在pom文件中增加agent包依赖:
 <dependency>
            <groupId>com.alibaba.ei</groupId>
            <artifactId>agentDemo</artifactId>
            <version>${agentDemo.version}</version>
</dependency>
  1. 在主测试类AgentTest中增加如下的测试方法
    在程序运行后加载,我们不可能在主程序中编写加载的代码,只能另写程序,那么另写程序如何与主程序进行通信?这里用到的机制就是attach机制,它可以将JVM A连接至JVM B,并发送指令给JVM B执行,JDK自带常用工具如jstack,jps等就是使用该机制来实现的。这里我们先用tomcat启动一个程序用作主程序B,再来写A程序代码。

我们使用VirtualMachine attach到目标进程,其中23764为tomcat进程的PID,可以使用jps命令获得,也可以使用VirtualMachine.list方法获取本机上所有的Java进程,再来判断Tomcat进程。很明显,这里的catalina进程就是我们在寻找的Tomcat进程,从而获取到本地Tomcat的进程号。

image.png

loadAgent方法第一个参数为Jar包在本机中的路径,第二个参数为传入agentmain的args参数,此处为null,运行程序。

 private static void test3() {
        try {
            List<VirtualMachineDescriptor> virtualMachineDescriptorList = VirtualMachine.list();
            String tomcatPID = "";
            for (VirtualMachineDescriptor descriptor : virtualMachineDescriptorList) {
                System.out.println(descriptor.id() + "," + descriptor.displayName());
                if ((descriptor.displayName().startsWith("org.apache.catalina.startup.Bootstrap start"))) {
                    tomcatPID = descriptor.id();
                    break;
                }
            }

            VirtualMachine vm = VirtualMachine.attach(tomcatPID);
            vm.loadAgent("/Users/XXX/Documents/code/javaagent/agentDemo/target/agentDemo-1.0-SNAPSHOT.jar");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

然而什么都没有打印啊!是不是什么地方写错了呢?仔细想想就会发现,我们是将进程attach到了tomcat进程上,agent其实是在主程序B中运行的,所以程序A中自然就不会进行打印,我们跳回tomcat程序的控制台,查看结果。

image.png

可以看到,agentmain方法中的代码已经在主程序中顺利运行了,并且打印出了程序中加载的类!

参考资料

  1. Java Agent简介

相关文章

网友评论

      本文标题:JavaAgent探针技术第二篇:在主程序运行之后的代理程序

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