java agent实现热部署

Java agent技术

1. Javaagent定义
  1. Javaagent是Java虚拟机(JVM)提供的一种机制,允许在运行时修改、增强或监控Java应用程序的行为

    1. Java agent 是一种特殊的Java程序(Jar文件),它是 Instrumentation 的客户端。与普通 Java 程序通过main方法启动不同,agent 并不是一个可以单独启动的程序,而必须依附在一个Java应用程序(JVM)上,与它运行在同一个进程中,通过 Instrumentation API 与虚拟机交互。
      3. Java agent 有两个启动时机,一个是在程序启动时通过 -javaagent 参数启动代理程序,一个是在程序运行期间通过 Java Tool API 中的 attach api 动态启动代理程序。
2. JVM启动时静态加载

​ 对于VM启动时加载的 agent,Instrumentation 会通过 premain 方法传入代理程序,premain 方法会在程序 main 方法执行之前被调用。此时大部分Java类都没有被加载(“大部分”是因为,agent类本身和它依赖的类还是无法避免的会先加载的),是一个对类加载埋点做手脚(addTransformer)的好机会。但这种方式有很大的局限性,Instrumentation 仅限于 main 函数执行前,此时有很多类还没有被加载,如果想为其注入 Instrumentation 就无法办到。

public static void premain(String args, Instrumentation inst) {

}
3. JVM 启动后动态加载

对于VM启动后动态加载的 agent,Instrumentation 会通过 agentmain 方法传入代理程序,agentmain 在 main 函数开始运行后才被调用。

public static void agentmain(String args, Instrumentation inst) {
       
}
  1. agent连接目标程序的两种方式
    1. jvm参数启动(premain只能以这种方式启动)
    2. attach接入

    jvm参数启动

    -javaagent:C:\\Users\\kafka.xin\\Desktop\\agent\\target\\agent-1.0-SNAPSHOT-jar-with-dependencies.jar
    

    attach接入

    本地调试可以通过下面代码获取pid,linux可用相关命令查询

    ManagementFactory.getRuntimeMXBean().getName().split("@")[0]
    

    attach的方式接入(依赖tools.jar)

    VirtualMachine vm = VirtualMachine.attach("17164");
                vm.loadAgent("path/agent.jar");
                vm.detach();
    
5. agent程序打包成jar
  1. 使用maven插件打包
  2. 通过MANIFEST.MF文件指定

使用maven插件打包

<plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4</version>
                <executions>

                    <execution>
                        <goals>
                            <goal>attached</goal>
                        </goals>
                        <phase>package</phase>
                        <configuration>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                            <archive>
                                <manifestEntries>
                                    <Premain-Class>com.agent.AgentMainTraceAgent</Premain-Class>
                                    <Agent-Class>com.agent.AgentMainTraceAgent</Agent-Class>
                                    <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                    <Can-Retransform-Classes>true</Can-Retransform-Classes>
                                </manifestEntries>
                            </archive>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
6. 重新加载类

可以使用Instrumentation::redefineClasses方法重新加载类,入参为ClassDefinition对象,伪代码如下:

 //获取字节码
byte[] bytes = loadClassByPath(sysPath);
//获取已加载的类
Class[] classes = inst.getAllLoadedClasses();
//根据已加载的类找到旧的目标类
Class oldClass = findOldClass(classes);
//重新加载类
inst.redefineClasses(new ClassDefinition(oldClass, bytes));
  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值