美文网首页
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