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

插件的结构

之前我们介绍了插件的加载,接下来就是真正开始进行插件的执行了,首先要看下插件的结构是怎么样的,以阿里的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,方法识别

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【PG】PostgreSQL高可用方案repmgr部署(非常详细)

目录 简介 1 概述 1.1 术语 1.2 组件 1.2.1 repmgr 1.2.2 repmgrd 1.3 Repmgr用户与元数据 2 安装部署 2.0 部署环境 2.1 安装要求 2.1.1 操作系统 2.1.2 PostgreSQL 版本 2.1.3 操作系统用户 2.1.4 安装位置 2.1.5 版本要求 2.2 安装 2.2.1 软件包安装 2.2…

使用Filebeat+Kafka+Logstash+Elasticsearch构建日志分析系统

随着时间的积累&#xff0c;日志数据会越来越多&#xff0c;当您需要查看并分析庞杂的日志数据时&#xff0c;可通过FilebeatKafkaLogstashElasticsearch采集日志数据到Elasticsearch中&#xff0c;并通过Kibana进行可视化展示与分析。本文介绍具体的实现方法。 一、背景信息 …

科学上网导致Adobe软件运行弹出This non-genuine Adobe app will be disabled soon,尝试解决办法

之前介绍用防火墙拦截Adobe软件的出站规则可以解决软件的非正版弹窗&#xff0c;但是有的用户却不行是为什么&#xff0c;原因是使用了代理网络。因为Adobe此时跑的不是本地的流量而是代理的流量。所以防火墙拦截就不起作用了。 首先是之前介绍过的拦截方法&#xff0c;如果你没…

百度飞浆环境安装

前言&#xff1a; 在安装飞浆环境之前得先把pytorch环境安装好&#xff0c;不过关于pytorch网上教程最多的都是通过Anaconda来安装&#xff0c;但是Anaconda环境安装容易遇到安装超时导致安装失败的问题&#xff0c;本文将叫你如何通过pip安装的方式快速安装&#xff0c;其实这…

14——1

这句话的意思是&#xff0c;如图中月份12天数23时&#xff0c;就是1223&#xff1b;当月份9天数2时&#xff0c;就是0902. 可以看到在上面给出的数组元素中&#xff0c;并没有连续挨在一起的2023数字元素——就有人可能输出答案0。 所以这里要看一下—— ——子序列的含义&…

The 8th China Open Source Conference Successfully Concludes

由开源社主办的第八届中国开源年会&#xff08;COSCon23&#xff09;于 2023年10月29日在成都圆满收官。本次大会&#xff0c;为期两天&#xff0c;线下参会报名逾千人次&#xff0c;在线直播观看人数总计 168610 人&#xff0c;直播观看次数达 248725 次&#xff0c;官网累计浏…

网络编程 —— TCP 和 UDP 编程详解

目录 网络编程主要函数介绍 1. socket 函数 2. bind 函数 3. listen 函数 4. accept 函数 5. connect 函数 6. send 函数 7. recv 函数 8. recvfrom 函数 9. sendto 函数 TCP 和 UDP 原理上的区别 TCP 编程 服务端代码&#xff1a; 客户端代码&#xff1a; UDP 编…

nodejs+vue公益帮学网站的设计与实现-微信小程序-安卓-python-PHP-计算机毕业设计

在当今高度发达的信息中&#xff0c;信息管理改革已成为一种更加广泛和全面的趋势。为确保中国经济的持续发展&#xff0c; 如何用方便快捷的方式使管理者在广阔的数据海洋里面查询、存储、管理和共享有效的数据信息&#xff0c;对我们的学习&#xff0c;工作和生活具有重要的现…

创造者设计模式

Bike package com.jmj.pattern.builder.demo01;public class Bike {private String frame;//车架private String seat;//车座public String getFrame() {return frame;}public void setFrame(String frame) {this.frame frame;}public String getSeat() {return seat;}public…

Webpack Bundle Analyzer包分析器

当我们需要分析打包文件dist里哪些资源可以进一步优化时&#xff0c;就可以使用包分析器插件webpack-bundle-analyzer。NPM上的介绍是使用交互式可缩放树图可视化 webpack 输出文件的大小。 我的是vue2项目。 1、webpack-bundle-analyzer插件的安装 $ npm install --save-dev…

接口测试vs功能测试

接口测试和功能测试的区别&#xff1a; 本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做和如何做&#xff1f;并没有解释为什…

Kyligence 入选 Gartner® 2023 客户之声报告,高分获评“卓越表现者”

近日&#xff0c;Gartner 发布了最新的《2023 分析和商业智能平台“客户之声”报告》(Voice of the Customer for Analytics and Business Intelligence Platforms, 2023, October 2023)。跬智信息&#xff08;Kyligence&#xff09;成功入选该报告&#xff0c;并凭借 4.7 分&a…

嵌入式养成计划-54----ARM--异常处理流程

一百三十五、异常处理流程 135.1 arm处理器工作模式 135.2 异常源和异常模式关系 135.2.1 异常源 异常源就是引发处理器进入相应异常模式 135.2.2 对应关系 异常模式异常源FIQ模式FIQ类型异常源引发处理器进入FIQ模式IRQ模式IRQ类型异常源引发处理器进入IRQ模式SVC模式上电…

opencv车牌识别<一>

目录 一、概述 二、ANPR简介 一、概述 本文将介绍创建自动车牌识别(Automatic Number Plate Recognition&#xff0c;ANPR)所需的步骤。对于不同的情形&#xff0c;实现自动车牌识别会用不同的方法和技术&#xff0c;例如&#xff0c;IR 摄像机、固定汽车位置、光照条件等…

spring cloud之配置中心

Config 统一配置中心(*) 1.简介 # 统一配置中心 - 官网:https://cloud.spring.io/spring-cloud-static/spring-cloud-config/2.2.3.RELEASE/reference/html/#_spring_cloud_config_server- config 分为 config server 和 config client。用来统一管理所有微服务的配置统一配置…

【Python 千题 —— 基础篇】欢迎光临

题目描述 题目描述 欢迎光临。为列表中的每个嘉宾打印欢迎光临语句。例如&#xff0c;有一份嘉宾列表 ["李二狗", "王子鸣"]&#xff0c;则需要根据嘉宾名单打印输出&#xff1a; 欢迎光临&#xff01;李二狗。 欢迎光临&#xff01;王子鸣。下面是一份…

基于JAX-WS的RESTful web服务返回通过JAXB注解生成的xml文档

基于JAX-WS编写的RESTful web服务&#xff0c;返回xml文档。这个xml文档可以基于JAXB注解的形式来生成&#xff0c;简化xml的生成。 例如&#xff0c;下面RegisterResponse 这个类使用了JAXB的注解&#xff1a; package com.thb.server.register;import jakarta.xml.bind.ann…

C语言--指针与数组--遍历数组的n种方式【详细】

一.一维数组名的含义 arr一般表示数组的其实地址&#xff08;除了两种例外&#xff09; 1.在定义数组的同一个函数中(不是形参),求sizeof(arr),求整个数组的字节数 2.在定义数组的同一个函数中(不是形参),&arr1,加整个数组的大小 (经常考试) 3.除上面以外,arr都表示数组的…

智能穿戴AR眼镜主板方案定制_MTK平台AR智能眼镜PCB板开发

AR智能眼镜&#xff0c;是采用了多种技术实现增强现实效果&#xff0c;是将虚拟信息和现实场景相结合的智能设备。 AR智能眼镜硬件上&#xff0c;包括多个传感器、显示装置和处理器等。其中&#xff0c;传感器用于捕捉用户的动作和环境信息&#xff0c;如摄像头、陀螺仪、加速…

【python】爬取酷狗音乐Top500排行榜【附源码】

一、导入必要的模块&#xff1a; 这篇博客将介绍如何使用Python编写一个爬虫程序&#xff0c;从斗鱼直播网站上获取图片信息并保存到本地。我们将使用requests模块发送HTTP请求和接收响应&#xff0c;以及os模块处理文件和目录操作。 如果出现模块报错 进入控制台输入&#xff…