Skywalking流程分析_4(插件的加载和不同版本的识别)

news2024/11/20 14:31:28

插件的结构

之前我们介绍了插件的加载,接下来就是真正开始进行插件的执行了,首先要看下插件的结构是怎么样的,以阿里的druid数据源为例

skywalking-plugin.def:

druid-1.x=org.apache.skywalking.apm.plugin.druid.v1.define.DruidPooledConnectionInstrumentation
druid-1.x=org.apache.skywalking.apm.plugin.druid.v1.define.DruidDataSourceInstrumentation
druid-1.x=org.apache.skywalking.apm.plugin.druid.v1.define.DruidDataSourceStatManagerInstrumentation

以第一个获取数据源连接的插件DruidPooledConnectionInstrumentation为例

DruidPooledConnectionInstrumentation

/**
 * 插件的定义,继承xxxPluginDefine,通常命名为xxxInstrumentation
 */
public class DruidDataSourceInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
    private static final String ENHANCE_CLASS = "com.alibaba.druid.pool.DruidDataSource";
    private static final String ENHANCE_METHOD = "getConnection";
    private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.druid.v1.PoolingGetConnectInterceptor";

    /**
     * 在哪个类进行字节码增强
     * */
    @Override
    protected ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }
    /**
     * 进行构造方法的拦截
     * */
    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    /**
     * 进行实例方法的拦截
     * */
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
                new InstanceMethodsInterceptPoint() {
                    //对getConnection无参方法记性增强
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return named(ENHANCE_METHOD).and(takesNoArguments());
                    }
                    //增强逻辑在哪个具体的插件类中执行
                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }

                    @Override
                    public boolean isOverrideArgs() {
                        return false;
                    }
                },
                new InstanceMethodsInterceptPoint() {
                    //对getConnection有参方法记性增强
                    @Override
                    public ElementMatcher<MethodDescription> getMethodsMatcher() {
                        return named(ENHANCE_METHOD).and(takesArguments(String.class, String.class));
                    }
                    //增强逻辑在哪个具体的插件类中执行
                    @Override
                    public String getMethodsInterceptor() {
                        return INTERCEPTOR_CLASS;
                    }
                    //在增强时是否要对原方法的入参进行改变
                    @Override
                    public boolean isOverrideArgs() {
                        return false;
                    }
                }
        };
    }

    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return new StaticMethodsInterceptPoint[0];
    }

}

ClassInstanceMethodsEnhancePluginDefine

如果对构造方法/实例方法增强,则需继承此类

public abstract class ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {

    /**
     * @return null, means enhance no static methods.
     */
    /**
     * 静态方法拦截点
     * */
    @Override
    public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
        return null;
    }

}

ClassStaticMethodsEnhancePluginDefine

如果对构造方法/实例方法增强,则需继承此类

public abstract class ClassStaticMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {

    /**
     * @return null, means enhance no constructors.
     */
    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return null;
    }

    /**
     * @return null, means enhance no instance methods.
     */
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return null;
    }
}

结构

  • ClassInstanceMethodsEnhancePluginDefineClassStaticMethodsEnhancePluginDefine都继承了ClassEnhancePluginDefine
  • ClassEnhancePluginDefine继承了AbstractClassEnhancePluginDefine

在返回要增强的类方法中ClassMatch就是要进行匹配的策略,有名字匹配、前缀匹配等,这里就不做详细分析了

@Override
protected ClassMatch enhanceClass() {
    return byName(ENHANCE_CLASS);
}

这里我们知道了如何指定在哪个类,对哪个方法进行增强,下面我们就来看看增强逻辑的类是怎么做的,仍然以阿里的druid数据源为例。指定了是org.apache.skywalking.apm.plugin.druid.v1.PoolingGetConnectInterceptor

PoolingGetConnectInterceptor

public class PoolingGetConnectInterceptor implements InstanceMethodsAroundInterceptor {

    @Override
    public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
        AbstractSpan span = ContextManager.createLocalSpan("Druid/Connection/" + method.getName());
        span.setComponent(ComponentsDefine.ALIBABA_DRUID);
    }

    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
        ContextManager.stopSpan();
        return ret;
    }

    @Override
    public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
        ContextManager.activeSpan().errorOccurred().log(t);
    }
}

可以看到和APO非常的像,前置/后置/异常增强

总结

  • 插件的定义
    • 位置,resources/skywalking-plugin.def
    • 内容,插件名=插件类定义
  • 插件的结构
    • 对构造/实例方法增强,继承ClassInstanceMethodsEnhancePluginDefine
    • 对静态方法增强,继承ClassStaticMethodsEnhancePluginDefine
    • ClassInstanceMethodsEnhancePluginDefineClassStaticMethodsEnhancePluginDefine都继承ClassEnhancePluginDefine,ClassEnhancePluginDefine继承AbstractClassEnhancePluginDefine
    • 指定要拦截的类的方法,enhanceClass()
    • 指定要拦截的构造方法,getConstructorsInterceptPoints
    • 指定要拦截的实例方法,getInstanceMethodsInterceptPoints
    • 指定要拦截的静态方法,getStaticMethodsInterceptPoints
  • 目标类的匹配
    • 按类名匹配,NameMatch
    • 间接匹配,IndirectMatch
      • PrefixMatch,前缀匹配
      • MethodAnnotationMatch,注解匹配
  • 进行拦截的定义方法
    • beforeMethod
    • afterMethod
    • handleMethodException

版本识别

skywalking是把不同版本的框架,来分别设置插件来对应着不用的版本,就拿常见的Srping来说,插件结构为

  • mvc-annotation-3.x-plugin
  • mvc-annotation-4.x-plugin
  • mvc-annotation-5.x-plugin

但有个关键的问题,skywalking是怎么识别出不同的Spring版本来执行对应版本的插件?

skywalking对这个问题的处理很巧妙,就是判断当前的类加载器中在相应的版本是否有对应类和方法

类识别

判断是否存在一个或多个类仅同时存在于某一个版本中

  • 在插件生效前会判断当前的版本是否存在对应的类
  • 假设应用中使用的是Spring 5.x,那么Srping-5.x-plugin判断确实存在C类,那么此版本就会生效
  • 而Srping-3.x-plugin和Srping-4.x-plugin不存在C类,所以Srping-5.x-plugin就不会加载
  • 当spring-v3-plugin插件生效前,判断应用中同时存在A、B两个类,满足该条件,所以spring-v3-plugin插件生效
  • 当spring-v4-plugin、spring-v5-plugin插件生效前,判断应用中同时存在B、C两个类,不满足该条件,所以spring-v4-plugin、spring-v5-plugin插件不生效

方法识别

当判断版本之间的类都相同是,类识别就没有办法了,这时就需要方法识别

  • 如图,假设只有Spring3.x存在A类,Spring4.x和Spring5.x类都相同,但Spring4.x的A类存在test方法,返回类型为Integer,入参类型为IntegerString。Spring5.x的A类也存在test方法,但返回类型为String,入参类型为String
  • 在插件生效前就会判断如何当前A类存在Intger test(Integer,String),那么Spring-4.x-plugin生效
  • 如何当前A类存在String test(String),那么Spring-5.x-plugin生效

skywalking的真正版本识别

  • witnessClasses就是类识别
  • witnessMethods就是方法识别
  • 这两个方法都在插件的顶级接口AbstractClassEnhancePluginDefine定义
public abstract class AbstractClassEnhancePluginDefine {

protected String[] witnessClasses() {
    return new String[] {};
}

protected List<WitnessMethod> witnessMethods() {
    return null;
}

那么skywalking到底是怎么判断类是否存在呢?

  • 这些插件都是由AgentClassLoader加载的
  • AgentClassLoader的父类加载器是AppClassLoader
  • 通过双亲委派机制,AgentClassLoader中找不到要识别的类就会向上委派给AppClassLoader
  • AppClassLoader找不到就向上委派,知道顶级的BootStrapClassLoader
  • 通过这种方式就能判断要识别的类是否存在
让我们以Spring为例看看skywalking到底是怎么判断的

类识别

Spring3.x

public abstract class AbstractSpring3Instrumentation extends ClassInstanceMethodsEnhancePluginDefine {

    public static final String WITHNESS_CLASSES = "org.springframework.web.servlet.view.xslt.AbstractXsltView";

    @Override
    protected final String[] witnessClasses() {
        return new String[] {WITHNESS_CLASSES};
    }
}

Srping4.x

public abstract class AbstractSpring4Instrumentation extends ClassInstanceMethodsEnhancePluginDefine {
    public static final String WITHNESS_CLASSES = "org.springframework.cache.interceptor.SimpleKey";

    @Override
    protected String[] witnessClasses() {
        return new String[] {
            WITHNESS_CLASSES,
            "org.springframework.cache.interceptor.DefaultKeyGenerator"
        };
    }
}

Spring5.x

public abstract class AbstractSpring5Instrumentation extends ClassInstanceMethodsEnhancePluginDefine {
    public static final String WITNESS_CLASSES = "org.springframework.web.servlet.resource.HttpResource";

    @Override
    protected final String[] witnessClasses() {
        return new String[] {WITNESS_CLASSES};
    }
}

可以看到Spring3.x、Spring4.x、Spring5.x分别都有不同要识别的类

方法识别

dubbo2.7.x

public class DubboInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {

    private static final String ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorFilter";

    private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.asf.dubbo.DubboInterceptor";

    private static final String CONTEXT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContext";

    private static final String GET_SERVER_CONTEXT_METHOD_NAME = "getServerContext";


    @Override
    protected List<WitnessMethod> witnessMethods() {
        return Collections.singletonList(new WitnessMethod(
            CONTEXT_TYPE_NAME,
            named(GET_SERVER_CONTEXT_METHOD_NAME).and(
                returns(named(CONTEXT_TYPE_NAME)))
        ));
    }

}

dubbo3.x

public class DubboInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {

    public static final String CONTEXT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContext";

    public static final String GET_SERVER_CONTEXT_METHOD_NAME = "getServerContext";

    public static final String CONTEXT_ATTACHMENT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContextAttachment";


    @Override
    protected List<WitnessMethod> witnessMethods() {
        return Collections.singletonList(
            new WitnessMethod(
                CONTEXT_TYPE_NAME,
                named(GET_SERVER_CONTEXT_METHOD_NAME).and(
                    returns(named(CONTEXT_ATTACHMENT_TYPE_NAME)))
            ));
    }

}

可以看到是通过dubbo2.7.x和dubbo3.x的getServerContext方法返回的不同类型来判断

总结

  • witnessClasses,类识别
  • witnessMethods,方法识别

接下来开始分析字节码的增强过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1210143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二进制原码、反码、补码、移码

机器数&#xff1a;一个数在计算机中的二进制表示形式&#xff0c;称为这个数的机器数。符号位&#xff1a;机器数是带符号的&#xff0c;在计算机中用最高位作为符号位&#xff0c;0为正数&#xff0c;1为负数。真值&#xff1a;机器数由于含有符号位&#xff0c;所以机器数的…

基于群居蜘蛛算法优化概率神经网络PNN的分类预测 - 附代码

基于群居蜘蛛算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于群居蜘蛛算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于群居蜘蛛优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

完整版指南:企业网络中的VXLAN-BGP-EVPN

随着互联网的发展&#xff0c;数据中心的数量和规模呈爆炸性增长趋势。数据中心业务不断增加&#xff0c;用户需求不断提高。随之而来的问题是数据中心的功能变得越来越复杂&#xff0c;运维管理变得越来越困难。VXLAN-BGP-EVPN的出现为企业网络带来了无限的可能性。 什么是VX…

中科驭数荣获北京市科学技术进步奖二等奖

近日&#xff0c;北京市人民政府发布京政发〔2023〕23号公告&#xff0c;2022年度北京市科学技术奖授奖清单揭晓&#xff0c;中国科学院计算技术研究所、北京控制工程研究所、中科驭数&#xff08;北京&#xff09;科技有限公司等产学研单位提报的“领域专用处理器关键技术及应…

竞赛 题目:基于LSTM的预测算法 - 股票预测 天气预测 房价预测

文章目录 0 简介1 基于 Keras 用 LSTM 网络做时间序列预测2 长短记忆网络3 LSTM 网络结构和原理3.1 LSTM核心思想3.2 遗忘门3.3 输入门3.4 输出门 4 基于LSTM的天气预测4.1 数据集4.2 预测示例 5 基于LSTM的股票价格预测5.1 数据集5.2 实现代码 6 lstm 预测航空旅客数目数据集预…

Linux 关闭对应端口号进程

查看当前的端口号是否在运行 找出端口号端口号进程 netstat -anp | grep 9000 关闭端口号 kill -9 [PID]

SAP ABAP 主动调用外部系统的REST接口(x-www-form-urlencoded)

如何在SAP ECC中调用外部系统提供的REST接口地址&#xff1f; Postman中使用Body中参数情况&#xff0c;使用链接的情况 x-www-form-urlencoded POST成功调用样例如下&#xff1a; SAP中实现如下&#xff1a; 1. 事务码STRUST,导入对方系统证书 2. 事务码SM59配置destinati…

hadoop 大数据集群环境配置 配置hadoop配置文件 hadoop(七)

1. 虚拟机的三台机器分别以hdfs 存储, mapreduce计算&#xff0c;yarn调度三个方面进行集群配置 hadoop 版本3.3.4 官网&#xff1a;Hadoop – Apache Hadoop 3.3.6 jdk 1.8 三台机器尾号为&#xff1a;22&#xff0c; 23&#xff0c; 24。&#xff08;没有用hadoop102, 103,10…

【kafka】windows安装启动

1.zookeeper的安装与启动 快速打开window powershell&#xff1a; windowx&#xff0c;选 2.kafka下载 —注意kafka和zookeeper需要版本匹配 安装路径 注意&#xff0c;kafka安装目录不能有空格。文件下载到&#xff1a; D:\Program_Files\kafka_2.12-3.6.0新建logs文件 修改c…

232.用栈实现队列(LeetCode)

思路 思路&#xff1a;利用两个栈实现队列先进先出的特性&#xff0c;先将元素导入一个栈内 模拟出队时&#xff0c;则将所有元素导入另一个栈内&#xff0c;此时元素顺序被反转过来&#xff0c;只需要取栈顶数据即可 那我们就可以将两个栈的功能分开&#xff0c;一个专门入pus…

verdi merge fsdb出现信号冲突的解决办法

前段时间介绍了verdi用 Edit Virtual File的方式把几个fsdb文件merge起来的方法 由于当时实验的时候只用了两个小的fsdb文件&#xff0c;每个fsdb文件中包含的信号量也比较少&#xff0c;所以并没有发现问题 我是用 Edit Virtual FIle把dump不同hier的fsdb文件merge到一起&am…

影响因子10月修正!多本期刊上涨,最高IF达54.8!

【SciencePub学术】 每年的影响因子基本都在6月底发布&#xff0c;但是由于数据不全等原因&#xff0c;部分期刊未能及时获得影响因子&#xff0c;或者影响因子有一定误差。因此&#xff0c;每年科睿唯安还会在10或11月份对当年的影响因子进行更新&#xff0c;主要包括补录和修…

驾驭数据与人工智能是人才培养的时代命题

2023年11月11日全国近千名计算机教育工作者共聚“海南博鳌亚洲论坛大酒店”&#xff0c;以“产教融合&#xff0c;供需共赢”为主题&#xff0c;“服务国家创新驱动发展&#xff0c;顺应全球新一轮科技革命和产业变革的趋势&#xff0c;培养集学科、技术和产业需求相融合的IT新…

Java封装一个根据指定的字段来获取子集的工具类

工具类 ZhLambdaUtils SuppressWarnings("all") public class ZhLambdaUtils {/*** METHOD_NAME*/private static final String METHOD_NAME "writeReplace";/*** 获取到lambda参数的方法名称** param <T> parameter* param function functi…

RK3588平台开发系列讲解(项目篇)实时显示摄像头

文章目录 一、测试代码二、代码解析2.1、OpenCV头文件2.2、类与函数的访问方式2.3、捕获摄像头图像2.4、定义图像变量2.5、显示图像沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将给大家介绍,如何基于USB摄像头进行实时显示。 一、测试代码 #include "o…

vue离线地图(瓦片)

最近公司要弄一个这样的离线地图&#xff0c;要求在图上打点画线之类的。折腾了几天&#xff0c;学习了三种方式&#xff1a; 1.拿到各省市区的经纬度json&#xff0c;通过echarts来制作&#xff0c;再套一个卫星图的地图背景 2.下载地图瓦片&#xff0c;再通过百度/高德的离线…

文章发表 | 求臻医学发布精准肿瘤学临床试验预筛选平台

近日&#xff0c;求臻医学信息与人工智能团队研发的精准肿瘤学临床试验预筛选平台OncoCTMiner&#xff0c;在线发表于国际期刊Database: The Journal of Biological Databases and Curation (IF5.8)。OncoCTMiner集成自然语言处理&#xff08;NLP&#xff09;和大型语言模型&am…

caspp attacker lab

attacker lab phase2 advice phase 1 ctarget 会先调用test , test调用getbuf, getbuf调用Get。 任务目的是通过缓冲区注入攻击&#xff0c;将函数getbuf返回直接重定向到函数touch1。 0x28 是 40 比特&#xff0c; gdb ./ctarget getbuf 下一次执行的指令是401976, rsp对…

【Python】上市公司数据进行经典OLS回归实操

一、题目二、数据合并、清洗、描述性统计1、数据获取2、数据合并3、选择董监高薪酬作为解释变量的理论逻辑分析 三、多元回归模型的参数估计、结果展示与分析1、描述性统计分析2、剔除金融类上市公司3、对所有变量进行1%缩尾处理4、0-1标准化&#xff0c;所有解释变量5、绘制热…

计算机毕设 机器学习股票大数据量化分析与预测系统 - python 计算机毕设

文章目录 0 前言1 课题背景2 实现效果UI界面设计web预测界面RSRS选股界面 3 软件架构4 工具介绍Flask框架MySQL数据库LSTM 5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业…