SkyWalking系列学习之环境搭建以及分析如何类增强

前言

  据了解公司使用SkyWalking做链路日志指标记录,并且由devlops团队配置,对业务开发无感。但处于好奇心,那必须拉下源码debug调试一番。

提出问题

  1. 怎样搭建源码调试环境?
  2. skywalking-java是如何利用java-agent做类增强?

问题分析

搭建源码调试环境

  1. 从github拉取skyWalking服务端的源码,切到最新分支v9.1.0,分别启动OAPServerBootstrap(链路指标上报服务)和ApplicationStartUp(UI)
    1.1 如果出现编译错误则设置 gRPC 的自动生成的代码目录,为源码目录
    在这里插入图片描述

    1.2 访问localhost:8080,进入以下页面表示服务端已经启动成功
    在这里插入图片描述

  2. 从github拉取skywalking-java的源码(agent,指标采集),切到最新分支v8.11.0。将其与业务代码保持同级(参考芋道源码文章
    在这里插入图片描述
    2.1 执行maven package打skywalking-java的jar包
    在这里插入图片描述
    2.2 在业务项目中配置vm options
    在这里插入图片描述

skywalking-java类增强源码分析

java agent

  1. 查看apm-sniffer/apm-agent的pom文件,在ManiFest属性中指定了“Premain-Class”
    在这里插入图片描述
  2. 在指定的Premain-Class中实现agent启动方法premain,在目标JVM启动的同时加载Agent
    在这里插入图片描述
  3. 在业务系统启动参数增加-javaagent:[path](其中path为对应的agent的jar包路径),就会执行SkyWalkingAgent#premain方法

bytebuddy类增强

  1. skywalking通过bytebuddy类增强

    public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {
    	
    	final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
    
        AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).ignore(
                nameStartsWith("net.bytebuddy.")
                        .or(nameStartsWith("org.slf4j."))
                        .or(nameStartsWith("org.groovy."))
                        .or(nameContains("javassist"))
                        .or(nameContains(".asm."))
                        .or(nameContains(".reflectasm."))
                        .or(nameStartsWith("sun.reflect"))
                        .or(allSkyWalkingAgentExcludeToolkit())
                        .or(ElementMatchers.isSynthetic()));
                        
         
         agentBuilder.type(pluginFinder.buildMatch())
         			// 类加载时会触发该转换器
                    .transform(new Transformer(pluginFinder))
                    .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                    .with(new RedefinitionListener())
                    .with(new Listener())
                    .installOn(instrumentation);
    }
    
  2. Transformer进行类转换
    2.1 找到原始类的插件定义列表:pluginDefines
    2.2 遍历pluginDefines,对原始类进行重新定义:AbstractClassEnhancePluginDefine#define

    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);
    }
    

    2.3 下面ClassEnhancePluginDefine#enhanceInstance是如何增强类的

    protected DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
        DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
        EnhanceContext context) throws PluginException {
        // 为原始类新增字段
    	newClassBuilder = newClassBuilder.defineField(
                    CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
                                                 .implement(EnhancedInstance.class)
                                                 .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
        //增强构造函数 --> ConstructorInter
        newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
                                                   .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
                                                                                                                 .to(new ConstructorInter(constructorInterceptPoint
                                                                                                                   .getConstructorInterceptor(), classLoader))));
    	//增强实例方法 --> InstMethodsInter
     	newClassBuilder = newClassBuilder.method(junction)
                                                         .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                    .to(new InstMethodsInter(interceptor, classLoader)));
    }
    
  3. 访问业务方法,发现InstMethodsInter进行拦截处理

    public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable<?> zuper,
        @Origin Method method) throws Throwable {
        // 前置处理
    	interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
    	// 业务方法
    	ret = zuper.call();
    	// 后置处理
    	ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
    }    
    

束语

  本篇文章是SkyWalking系列学习第一篇文章,主要介绍如何搭建源码调式环境以及了解skywalking是如何基于java-agent做类增强。后续为分析是如何增强的,怎样构建链路信息?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值