Skywalking流程分析_7(构造/实例方法的增强流程)

前言

this.enhance

java 复制代码
protected DynamicType.Builder<?> enhance(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
                                         ClassLoader classLoader, EnhanceContext context) throws PluginException {
    //静态方法的增强
    newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);
    //构造、实例方法的增强
    newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);

    return newClassBuilder;
}

在上文中我们分析的静态方法的增强是如何做的,接下来我们来分析构造和实例方法

ClassEnhancePluginDefine.enhanceInstance(typeDescription, newClassBuilder, classLoader, context)

java 复制代码
protected DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
    DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
    EnhanceContext context) throws PluginException {
    //查找构造方法的切点
    ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();
    //查找实例方法的切点
    InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();
    String enhanceOriginClassName = typeDescription.getTypeName();
    boolean existedConstructorInterceptPoint = false;
    if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) {
        existedConstructorInterceptPoint = true;
    }
    boolean existedMethodsInterceptPoints = false;
    if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {
        existedMethodsInterceptPoints = true;
    }

    /**
     * nothing need to be enhanced in class instance, maybe need enhance static methods.
     * 没有要增强的那么直接返回
     */
    if (!existedConstructorInterceptPoint && !existedMethodsInterceptPoints) {
        return newClassBuilder;
    }

    /**
     * Manipulate class source code.<br/>
     *
     * new class need:<br/>
     * 1.Add field, name {@link #CONTEXT_ATTR_NAME}.
     * 2.Add a field accessor for this field.
     *
     * And make sure the source codes manipulation only occurs once.
     * 
     */
    // 如果被拦截的类没有实现EnhancedInstance接口
    if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
        if (!context.isObjectExtended()) {
            //给要增强的类添加_$EnhancedClassField_ws字段,
            //并实现了EnhancedInstance接口的set/get方法,来获取这个字段
            //新增这个字段的作用是可以放置自己的数据,然后通过这两个set/get方法来存放和拿取
            newClassBuilder = newClassBuilder.defineField(
                CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
                                             .implement(EnhancedInstance.class)
                                             .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
            // 将记录状态EnhanceContext标识符设置为已新增新的字段或者实现新的接口                                 
            context.extendObjectCompleted();
        }
    }

    /**
     * 2. enhance constructors
     * 构造器增强逻辑
     */
    if (existedConstructorInterceptPoint) {
        //获取所有构造方法的切点
        for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {
            //要增加的类是Bootstrap类加载器加载的
            if (isBootstrapInstrumentation()) {
                newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
                                                 .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
                                                                                                             .to(BootstrapInstrumentBoost
                                                                                                                 .forInternalDelegateClass(constructorInterceptPoint
                                                                                                                     .getConstructorInterceptor()))));
            } else {
                newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
                                                 .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
                                                                                                             .to(new ConstructorInter(constructorInterceptPoint
                                                                                                                 .getConstructorInterceptor(), classLoader))));
            }
        }
    }

    /**
     * 3. enhance instance methods
     * 实例方法增强逻辑
     */
    if (existedMethodsInterceptPoints) {
        //获取所有实例方法的切点
        for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {
            String interceptor = instanceMethodsInterceptPoint.getMethodsInterceptor();
            if (StringUtil.isEmpty(interceptor)) {
                throw new EnhanceException("no InstanceMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
            }
            ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());
            if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {
                junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));
            }
            if (instanceMethodsInterceptPoint.isOverrideArgs()) {
                //要增加的类是Bootstrap类加载器加载的
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.method(junction)
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .withBinders(Morph.Binder.install(OverrideCallable.class))
                                                                                .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
                } else {
                    newClassBuilder = newClassBuilder.method(junction)
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .withBinders(Morph.Binder.install(OverrideCallable.class))
                                                                                .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));
                }
            } else {
                //要增加的类是Bootstrap类加载器加载的
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.method(junction)
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
                } else {
                    newClassBuilder = newClassBuilder.method(junction)
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .to(new InstMethodsInter(interceptor, classLoader)));
                }
            }
        }
    }

    return newClassBuilder;
}

总结

  • 给要增强的类添加_$EnhancedClassField_ws字段,并实现了EnhancedInstance接口的set/get方法,来获取这个字段。新增这个字段的作用是可以放置自己的数据,然后通过这两个set/get方法来存放和拿取
  • 执行构造器增强逻辑
  • 执行实例方法增强逻辑

下面我们来逐步分析构造器增强和实例方法增强

构造器增强

非JDK的类库是被ConstructorInter所增强的

java 复制代码
newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
                     .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
                                                         .to(new ConstructorInter(constructorInterceptPoint
                                                             .getConstructorInterceptor(), classLoader))));
ConstructorInter
java 复制代码
public class ConstructorInter {
    private static final ILog LOGGER = LogManager.getLogger(ConstructorInter.class);

    private InstanceConstructorInterceptor interceptor;

    /**
     * @param constructorInterceptorClassName class full name.
     */
    public ConstructorInter(String constructorInterceptorClassName, ClassLoader classLoader) throws PluginException {
        try {
            // 实例化自定义的拦截器
            //对于同一份字节码,如果由不同的类加载器进行加载,那么加载出来的两个实例不相同
            interceptor = InterceptorInstanceLoader.load(constructorInterceptorClassName, classLoader);
        } catch (Throwable t) {
            throw new PluginException("Can't create InstanceConstructorInterceptorV2.", t);
        }
    }

    /**
     * Intercept the target constructor.
     *
     * @param obj          target class instance.
     * @param allArguments all constructor arguments
     */
    @RuntimeType
    public void intercept(@This Object obj, @AllArguments Object[] allArguments) {
        try {
            //这里向上转型为EnhancedInstance的原因是为了使用之前新添加的_$EnhancedClassField_ws字段
            //使用setSkyWalkingDynamicField/getSkyWalkingDynamicField方法来存放和获取自己的数据
            EnhancedInstance targetObject = (EnhancedInstance) obj;
            //在原来的构造方法执行完后,再执行
            interceptor.onConstruct(targetObject, allArguments);
        } catch (Throwable t) {
            LOGGER.error("ConstructorInter failure.", t);
        }

    }
}

ConstructorInter逻辑总结

  • 构造方法中实例化自定义的拦截器
  • 在原先的构造方法执行完,再执行interceptor.onConstruct(targetObject, allArguments)

kafka为例

java 复制代码
public class KafkaProducerInstrumentation extends AbstractKafkaInstrumentation {

    public static final String ENHANCE_CLASS = "org.apache.kafka.clients.producer.KafkaProducer";
    public static final String CONSTRUCTOR_INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.kafka.ProducerConstructorInterceptor";
    public static final String CONSTRUCTOR_INTERCEPTOR_FLAG = "org.apache.kafka.clients.producer.ProducerConfig";

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[] {
            new ConstructorInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getConstructorMatcher() {
                    return takesArgumentWithType(0, CONSTRUCTOR_INTERCEPTOR_FLAG);
                }

                @Override
                public String getConstructorInterceptor() {
                    return CONSTRUCTOR_INTERCEPTOR_CLASS;
                }
            }
        };
    }

    @Override
    protected ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }
}
  • 这里拦截的是org.apache.kafka.clients.producer.KafkaProducer类的构造方法
  • 拦截具体的构造方法是第一个参数类型为org.apache.kafka.clients.producer.ProducerConfig
  • 拦截的执行类是org.apache.skywalking.apm.plugin.kafka.ProducerConstructorInterceptor
java 复制代码
public class ProducerConstructorInterceptor implements InstanceConstructorInterceptor {

    @Override
    public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
        ProducerConfig config = (ProducerConfig) allArguments[0];
        objInst.setSkyWalkingDynamicField(StringUtil.join(';', config.getList("bootstrap.servers")
                                                                     .toArray(new String[0])));
    }
}

注意

能看到这里使用_$EnhancedClassField_ws字段来保存一些数据方便后续使用,这也是为什么要执行这段逻辑,设置此字段的原因

java 复制代码
if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
  if (!context.isObjectExtended()) {
      //给要增强的类添加_$EnhancedClassField_ws字段,
      //并实现了EnhancedInstance接口的set/get方法,来获取这个字段
      //新增这个字段的作用是可以放置自己的数据,然后通过这两个set/get方法来存放和拿取
      newClassBuilder = newClassBuilder.defineField(
          CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
                                       .implement(EnhancedInstance.class)
                                       .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
      context.extendObjectCompleted();
  }
}
实例方法增强

首先看这段逻辑

java 复制代码
ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());
if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {
    //拦截到的方法必须是当前类上的 注解匹配到的方法有可能不是当前类上的
    junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));
}
  • 比如说,要对Student.add()方法增强,那么直接指定类名即可,但如果想对所有Controller的SpringMVC控制层的方法进行增强,那么就要使用注解。
  • 但有可能通过注解匹配到的方法不是当前类上的,所以判断如果拦截点为DeclaredInstanceMethodsInterceptPoint会新增匹配条件:获取到的方法必须是当前类上的
    非JDK的类库是被ConstructorInter所增强的

我们依旧先分析不是JDK类库的类

不修改原方法的参数
java 复制代码
newClassBuilder = newClassBuilder.method(junction)
                                .intercept(MethodDelegation.withDefaultConfiguration()
                                .to(new InstMethodsInter(interceptor, classLoader)));

具体逻辑在InstMethodsInter

InstMethodsInter

java 复制代码
public class InstMethodsInter {
    private static final ILog LOGGER = LogManager.getLogger(InstMethodsInter.class);

    /**
     * An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}
     * type will trigger classloader failure. If you want to know more, please check on books about Classloader or
     * Classloader appointment mechanism.
     */
    private InstanceMethodsAroundInterceptor interceptor;

    /**
     * @param instanceMethodsAroundInterceptorClassName class full name.
     */
    public InstMethodsInter(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
        try {
            //使用自定义加载器加载拦截器
            interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
        } catch (Throwable t) {
            throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
        }
    }

    /**
     * Intercept the target instance method.
     *
     * @param obj          target class instance.
     * @param allArguments all method arguments
     * @param method       method description.
     * @param zuper        the origin call ref.
     * @return the return value of target instance method.
     * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
     *                   bug, if anything triggers this condition ).
     */
    @RuntimeType
    public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,
        @Origin Method method) throws Throwable {
        EnhancedInstance targetObject = (EnhancedInstance) obj;

        MethodInterceptResult result = new MethodInterceptResult();
        try {
            //前置增强
            interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
        } catch (Throwable t) {
            LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
        }

        Object ret = null;
        try {
            if (!result.isContinue()) {
                ret = result._ret();
            } else {
                //执行原有方法
                ret = zuper.call();
            }
        } catch (Throwable t) {
            try {
                //异常增强
                interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
            } catch (Throwable t2) {
                LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
            }
            throw t;
        } finally {
            try {
                //后置增强
                ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
            } catch (Throwable t) {
                LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
            }
        }
        return ret;
    }
}
修改原方法的参数
java 复制代码
newClassBuilder = newClassBuilder.method(junction)
                  .intercept(MethodDelegation.withDefaultConfiguration()
                                 .withBinders(Morph.Binder.install(OverrideCallable.class))
                                 .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));

具体逻辑在InstMethodsInterWithOverrideArgs

InstMethodsInterWithOverrideArgs

java 复制代码
public class InstMethodsInterWithOverrideArgs {
    private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterWithOverrideArgs.class);

    /**
     * An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}
     * type will trigger classloader failure. If you want to know more, please check on books about Classloader or
     * Classloader appointment mechanism.
     */
    private InstanceMethodsAroundInterceptor interceptor;

    /**
     * @param instanceMethodsAroundInterceptorClassName class full name.
     */
    public InstMethodsInterWithOverrideArgs(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
        try {
            interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
        } catch (Throwable t) {
            throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
        }
    }

    /**
     * Intercept the target instance method.
     *
     * @param obj          target class instance.
     * @param allArguments all method arguments
     * @param method       method description.
     * @param zuper        the origin call ref.
     * @return the return value of target instance method.
     * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
     *                   bug, if anything triggers this condition ).
     */
    @RuntimeType
    public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Origin Method method,
        @Morph OverrideCallable zuper) throws Throwable {
        EnhancedInstance targetObject = (EnhancedInstance) obj;

        MethodInterceptResult result = new MethodInterceptResult();
        try {
            interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
        } catch (Throwable t) {
            LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
        }

        Object ret = null;
        try {
            if (!result.isContinue()) {
                ret = result._ret();
            } else {
                ret = zuper.call(allArguments);
            }
        } catch (Throwable t) {
            try {
                interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
            } catch (Throwable t2) {
                LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
            }
            throw t;
        } finally {
            try {
                ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
            } catch (Throwable t) {
                LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
            }
        }
        return ret;
    }
}

可以看到实例方法的增强逻辑和静态方法的大致相同

这里面有个问题,为什么用zuper.call()执行原方法的调用,不能替换成method.invoke(clazz.newInstance())吗?

这里需要借助一个Java实时反编译工具friday,看看项目执行SkyWalking Agent之后反编译的结构

原始类:

java 复制代码
@RestController
@RequestMapping("/api/hello")
public class UserController {

    @GetMapping
    public String sayHello() {
        return "hello";
    }

}

增强后:

java 复制代码
@RestController
@RequestMapping(value={"/test"})
public class TestController
implements EnhancedInstance {
    private volatile Object _$EnhancedClassField_ws;
    public static volatile /* synthetic */ InstMethodsInter delegate$mvblfc0;
    public static volatile /* synthetic */ InstMethodsInter delegate$hfbkh30;
    public static volatile /* synthetic */ ConstructorInter delegate$gr07501;
    private static final /* synthetic */ Method cachedValue$kkbY4FHP$ldstch2;
    public static volatile /* synthetic */ InstMethodsInter delegate$lvp69q1;
    public static volatile /* synthetic */ InstMethodsInter delegate$mpv7fs0;
    public static volatile /* synthetic */ ConstructorInter delegate$v0q1e31;
    private static final /* synthetic */ Method cachedValue$Hx3zGNqH$ldstch2;

    public TestController() {
        this(null);
        delegate$v0q1e31.intercept((Object)this, new Object[0]);
    }

    private /* synthetic */ TestController(auxiliary.YsFzTfDy ysFzTfDy) {
    }

    @RequestMapping
    public String test() {
        return (String)delegate$lvp69q1.intercept((Object)this, new Object[0], (Callable)new auxiliary.pEJy33Ip(this), cachedValue$Hx3zGNqH$ldstch2);
    }

    private /* synthetic */ String test$original$70VVkKcL() {
        return "sucess";
    }

    static {
        ClassLoader.getSystemClassLoader().loadClass("org.apache.skywalking.apm.dependencies.net.bytebuddy.dynamic.Nexus").getMethod("initialize", Class.class, Integer.TYPE).invoke(null, TestController.class, 544534948);
        cachedValue$Hx3zGNqH$ldstch2 = TestController.class.getMethod("test", new Class[0]);
    }

    final /* synthetic */ String test$original$70VVkKcL$accessor$Hx3zGNqH() {
        return this.test$original$70VVkKcL();
    }
}

然后还要再看一下SkyWalkingAgent.premain()

java 复制代码
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
    final PluginFinder pluginFinder;
    //省略...
                //指定byteBuddy要拦截的类
    agentBuilder.type(pluginFinder.buildMatch())
                //指定字节码增强的工具
                .transform(new Transformer(pluginFinder))
                //redefine和retransformation的区别在于是否保留修改前的内容
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(new RedefinitionListener())
                .with(new Listener())
                //将agent安装到instrumentation
                .installOn(instrumentation);
    //省略...            
}
  • redefineretransform的区别在于是否保留修改前的内容,从反编译的内容可以看到test()方法的内容已经被修改掉了,原方法被重命名为test o r i g i n a l original original+随机字符串
  • 新的test()方法中调用了InstMethodsInter的intercept()方法,最后传入的intercept()的method参数是被修改后的test()方法,不再指向原生的test()方法,如果在InstMethodsInter使用method.invoke(clazz.newInstance())就相当于自己调自己,就会死递归下去

总结

  • 构造方法
    • 是JDK类库的类
    • 不是JDK类库的类
      • 在原有的构造方法执行完后,在执行onContruct进行增强
  • 实例方法
    • 和静态方法相同

以上就是构造方法和实例方法增强逻辑。

在我们执行增强逻辑前,我们先看下插件拦截类的加载过程

  • 静态方法增强前的加载
java 复制代码
//clazz 要修改的字节码的原生类
StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz
            .getClassLoader());
  • 构造/实例方法前的加载
java 复制代码
//当前拦截到的类的类加载器
interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);

这个加载的方法很重要,它是将插件拦截类和业务执行类的类加载器进行融合的关键,也是skywalking的精髓所在,下篇文章我们来详细的分析下。

相关推荐
Linux-18741 小时前
分布式链路追踪系统之docker-compose安装skywalking
云原生·docker-compose·skywalking·分布式链路追踪系统·应用程序性能监控
流烟默13 小时前
SpringBoot应用链路追踪 traceId 技术方案
spring boot·skywalking·traceid
Linux-18741 天前
分布式链路追踪系统之二进制安装skywalking
elasticsearch·云原生·skywalking·分布式链路追踪系统·应用程序性能监控
令狐前生4 天前
SpringBoot3项目集成Skywalking示例
skywalking
乒乓狂魔6 天前
SkyWalking 也能 AI 智能化了
人工智能·skywalking
摇滚侠14 天前
Skywalking 应用性能监控和可观测性分析平台 简介
skywalking
Databuff14 天前
skywalking 快速使用入门
skywalking
摇滚侠16 天前
Apache Skywalking 实战 阅读笔记 第三章
笔记·apache·skywalking
摇滚侠17 天前
Apache Skywalking 实战 阅读笔记 第二章
笔记·apache·skywalking
Databuff17 天前
国产开源APM databuff 成为 CNCF 顶级项目opentelemetry 官宣Vendor
运维·开源·skywalking·apm·可观测性·opentelemetry