基础准备

现在,我们需要一个程序入口,随时挂接具体的java应用和刚刚开发的agent。
首先,我们考虑java自身提供的功能:监控能力,主要包在 sun.jvmstat下,可以拿到当时正在运行的所有java进程,如此便可以让系统自动找到我们的测试java应用,避免每次去查看系统的进程号。
其次,我们考虑怎么动态的加载agent,目前来说最好的方式有两种,一个是自己引用tools.jar,使用类
com.sun.tools.attach.VirtualMachine加载,另外一种,就是借助第三方工具类,比如bytebuddy,目前我们先采用方式一,后面我们会详细讲解方式二。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <parent>
  5. <artifactId>jvm_sandbox_demo</artifactId>
  6. <groupId>com.deer</groupId>
  7. <version>1.0-SNAPSHOT</version>
  8. <relativePath>../pom.xml</relativePath>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <groupId>com.deer.agent.starter</groupId>
  12. <artifactId>jvm_sandbox_agent_starter</artifactId>
  13. <name>jvm_sandbox_agent_starter</name>
  14. <properties>
  15. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  16. <maven.compiler.source>1.8</maven.compiler.source>
  17. <maven.compiler.target>1.8</maven.compiler.target>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>com.sun</groupId>
  22. <artifactId>tools</artifactId>
  23. <version>1.8</version>
  24. <scope>system</scope>
  25. <systemPath>/Users/yudongwei/Library/Java/JavaVirtualMachines/corretto-1.8.0_322/Contents/Home/lib/tools.jar</systemPath>
  26. </dependency>
  27. </dependencies>
  28. </project>
  1. package com.deer.agent.starter;
  2. import com.sun.tools.attach.VirtualMachine;
  3. import sun.jvmstat.monitor.*;
  4. import java.net.URISyntaxException;
  5. import java.util.HashSet;
  6. import java.util.Set;
  7. public class AgentStarter {
  8. public static void main(String[] args) throws URISyntaxException, MonitorException {
  9. String processId = null;
  10. // 获取监控主机
  11. MonitoredHost local = MonitoredHost.getMonitoredHost("localhost");
  12. // 取得所有在活动的虚拟机集合
  13. Set<Integer> vmlist = new HashSet<Integer>(local.activeVms());
  14. // 遍历集合,找到我们系统启动需要挂载的PID和进程名
  15. for(Integer process : vmlist) {
  16. MonitoredVm vm = local.getMonitoredVm(new VmIdentifier("//" + process));
  17. // 获取类名
  18. String processName = MonitoredVmUtil.mainClass(vm, true);
  19. if(processName.equals("com.deer.base.BaseApp")){
  20. processId = process.toString();
  21. break;
  22. }
  23. }
  24. if(processId == null){
  25. return;
  26. }
  27. System.out.println("find processId ,and ready to attach..."+processId);
  28. String agentPath="/Users/yudongwei/IdeaProjects/jvm_sandbox_demo/jvm_sandbox_agent/target/jvm_sandbox_agent-1.0-SNAPSHOT-jar-with-dependencies.jar";
  29. try {
  30. VirtualMachine vm = VirtualMachine.attach(processId);
  31. vm.loadAgent(agentPath);
  32. }catch (Exception e){
  33. e.printStackTrace();
  34. }
  35. }
  36. }

测试效果

启动 spring 应用:com.deer.base.BaseApp
打包agent
启动入口:com.deer.agent.starter.AgentStarter

基础测试

image.png
image.png
此时,我们看到动态加载的方法执行了

功能测试

我们用浏览器访问

  1. http://localhost:9090/sayHi/zhangfei

image.png

新的问题

此时我们发现,自己定义的类有一个被agent访问到了(com.deer.base.service.BaseService),另外一个没有被agent访问到(com.deer.base.service.impl.HelloServiceImpl)
是我们代码写错了,还是另有玄机?
是不是因为spring的bean使用java代理,此时类名已经不匹配了?
这些都会随着接下来的课程一步一步抽丝剥茧解决。