设为首页 加入收藏

TOP

记一次多个Java Agent同时使用的类增强冲突问题及分析(一)
2023-07-25 21:31:54 】 浏览:69
Tags:Java Agent 时使用 强冲突
摘要:Java Agent技术常被用于加载class文件之前进行拦截并修改字节码,以实现对Java应用的无侵入式增强。

本文分享自华为云社区《记一次多个JavaAgent同时使用的类增强冲突问题及分析》,作者:Vansittart。

问题背景

Java Agent技术常被用于加载class文件之前进行拦截并修改字节码,以实现对Java应用的无侵入式增强。Sermant是致力于服务治理领域的开源Java Agent框架项目。某客户在集成Sermant之前已集成了两套Java Agent:用于业务能力增强的自研Java Agent和用于链路采集的SkyWalking。该客户单独挂载自研Java Agent插件包时,字节码增强可以按照预期生效。后期引入开源SkyWalking并同时将自研Java Agent插件包和SkyWalking通过-javaagent启动参数挂载至业务应用中。使用过程中发现,两者的加载顺序会对预期的拦截点增强生效与否有直接影响。为什么会产生这种现象?该客户求助Sermant社区寻求解决多个JavaAgent的增强冲突问题,以避免类似典型问题再次出现以及顺利集成Sermant用于业务的服务治理。

笔者尝试从字节码增强的底层逻辑的角度来分析该问题的症结。

挂载多个JavaAgent的增强冲突问题

引入SkyWalking的初衷,是希望自研JavaAgent对业务的增强和SkyWalking的链路追踪能力都能正常在业务应用上生效。-javaagent参数是支持多次执行的,所以因此在启动应用时在JAVA_TOOL_OPTIONS中加上了-javaagent:/xxx/my-agent.jar和-javaagent:/xxx/skywalking-agent.jar参数。

先加载自研JavaAgent后加载SkyWalking

在测试时首先把自研JavaAgent放在前面,SkyWalking放在后面, 即-javaagent:/xxx/my-agent.jar -javaagent:/xxx/SkyWalking-agent.jar。应用启动前执行的逻辑如下图所示。按照参数的配置顺序,应该是自研JavaAgent先对业务应用的jar包中字节码进行增强,然后再由SkyWalking进行增强,最后再执行业务应用的main()方法启动应用。

然而启动后发现日志中SkyWalking抛出java.lang.UnsupportedOperationException异常,该异常对应的目标类是com.google.common.eventbus.Dispatcher$LegacyAsyncDispatcher。自研JavaAgent无异常抛出。

ERROR 2022-09-27 15:32:09:546 main SkyWalkingAgent : index=0, batch=[class com.google.common.eventbus.Dispatcher$LegacyAsyncDispatcher], types=[class com.google.common.eventbus.Dispatcher$LegacyAsyncDispatcher] 
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change superclass or interfaces
at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.SkyWalking.apm.dependencies.net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Dispatcher$ForJava6CapableVm.retransformClasses(AgentBuilder.java:6910)
... 12 more

经过确认自研JavaAgent并没有对这个类有过拦截和增强,而SkyWalking中的apm-guava-eventbus-plugin插件对该类进行了拦截和增强。两个JavaAgent并没有同时增强同一个类,但是SkyWalking却增强失败了,有点令人费解。初步猜测可能JavaAgent的加载顺序有关,笔者调整了顺序,再次进行了测试。

先加载SkyWalking后加载自研JavaAgent

调整后JAVA_TOOL_OPTIONS配置为-javaagent:/xxx/SkyWalking-agent.jar -javaagent:/xxx/my-agent.jar,应用启动前执行的逻辑如下图所示

经过调整后,发现两个JavaAgent都没有错误日志,而且各拦截点的增强也能正常生效,没有遇到类增强的冲突问题。

问题表象给人的直觉是JavaAgent的加载顺序确实对字节码增强有关系。但是为什么会出现这种现象呢?

冲突根因分析

增强失败的类在两个JavaAgent中的角色

上面提到,先加载自研JavaAgent后加载SkyWalking的场景中遇到SkyWalking对com.google.common.eventbus.Dispatcher$LegacyAsyncDispatcher增强失败。Dispatcher$LegacyAsyncDispatcher这个类在SkyWalking的插件中定义为被拦截增强的类。

经过排查发现Dispatcher$LegacyAsyncDispatcher也被自研JavaAgent中在增强过程中作为第三方依赖引入,但并未对其增强。

Debug分析

鉴于自研JavaAgent没有报错,但SkyWalking出现异常,所以对SkyWalking进行debug分析。

在premain方法中,可以看到进入到SkyWalkingAgent时``com.google.common.eventbus.Dispatcher`已经被加载了。观察它的类加载器,可以知道该类是在自研JavaAgent启动过程中被加载的。是不是被加载过后的类再进行增强就会冲突呢?接着往下看。

分析源码可知SkyWalki

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇java :多线程实现的三种方式 下一篇前后端分离项目解决前端跨域访问..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目