首先介绍一下JavaAgent的不同接入方式,这是Sermant实现动态接入能力的技术基础。Java 中Instrumentation API 提供了一种修改字节码的机制,利用该API,可以通过修改字节码的方式来改变程序的行为,而不用触及程序的源码。JavaAgent为Instrumentation API的客户端,通过JavaAgent可以调用API进行字节码的操作,其提供了两种加载方式给开发者重载:
这个问题的出现,根源在于JavaAgent通过agentmain方式加载到已运行的JVM中时,不同于静态加载,会在类初次被加载时完成字节码的转换,动态加载时一些需要被字节码增强类已经完成了类加载过程,这时候需要使用Instrumentation提供的类重转换(retransform classes)能力来修改字节码,在Instrumentation的Javadoc中关于这个能力有这样一段描述: “The retransformation must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance.(重转换过程中,我们不能新增、删除或者重命名字段和方法,不能更改方法的签名,不能更改类的继承。)”
从中可以看出,在引入动态加载能力前,优先要保证字节码增强时,不可以有上述内容中所描述的限制操作。
不过Sermant不太需要担心这个问题,因为这种限制不仅仅在动态加载时会触发,在多个JavaAgent同时使用时也可能会触发,可以参考Sermant团队的另一篇文章:《记一次多个JavaAgent同时使用的类增强冲突问题及分析》。为了保证在多Agent场景下的兼容性,Sermant的字节码增强模板严格遵循Instrumentation API的限制,因此Sermant在兼容性上的不断改进过程中无心插柳,帮助动态加载能力铺平了路。
3.2 如何保证在服务治理插件安装和卸载时不互相影响?