zoukankan      html  css  js  c++  java
  • Skywalking的增强与拦截机制

    整理自架构经理(汤哥)的分享

    字节增强条件匹配

    在 skywalking 中实现很多基于 byte-buddy 的关于链式匹配查询的实现, 代码如下所示:

    public abstract class AbstractJunction<V> implements ElementMatcher.Junction<V>

    其对应的类的类图关系如下所示:

    除此之外, 为了便于更好的执行在拦截时期的的匹配, skywalking  又定义了一套自实现的 Match  的匹配器, 类的简要类图关系如下所示:

    其中 NameMatch 名称 Match 的 NameMatch 好象相对类的继承相关较独立,但是其它方式的 Match 继承结构相对有点复杂, 都继承于IndirectMatch , 上面类的简意类图对应的类的完整类图详情如下所示:

    插件方式增强的扩展点

    所有的基于 skywalking 的整套增强逻辑框架下,如查有新的要增强的中间件或组件, 必须遵守其制定的插件扩展规范, 其具体的规范如下:

    一、 在对应的插件的 jar 包的 classpath 下必须存在定义文件 skywalking-plugin.def ,文件中必须包括 对 JTI 中的 Instrumentation 的自定义类的全限定名, 以 Dubbo 增强为例, 其内容为

    dubbo=org.apache.skywalking.apm.plugin.dubbo.DubboInstrumentation

    二、自定义的 Instrumentation 必须继承至 ClassInstanceMethodsEnhancePluginDefine 抽象类 , 在类中,须指定这个设施的增强的两个核心要素内容:

    A). 须要增强的目标类 , Enhance Class
    B). 插件中自己要实现的具体的拦截实现 xxxxxInterceptor
    同样以 Dubbo 为例, 如果要自定义自己的插件实的话,则就要指定如下内容:

    三、最后就是要真实的定义一个 实现接口 InstanceMethodsAroundInterceptor  的一个拦截实现

    对于每一个插件来说, 不一定只能有一个 Interceptor , 还可以有多个, 因为有时不一定只是要对一个类或一个方法进行拦截, 可能是多个类, 也有可能是一个类中的多个方法, 或多个类的多个方法,因此,在最初的 Instrumentation 入口定义之外,就提供了可以多个方法的增强扩展, 具体的如下图针对 Dubbo 的增强所示:

    插件方式增强主体逻辑

    理解实现插件增强机理的类与类之间的关系,犹为重要,它也是 sky-walking 的对于中间件以插件方式进行增强操作的核心, 下面我们就来仔细的分析下,是如何进行自定义插件, 并如何进行对中间件的增强的。在整个的增强的机制中, ClassEnhancePluginDefine 是很重要, 很核心的一个类, 在其 enhance 方法中, 分别指定了对

    • 静态方法增强
    • 实现方法增强
    • 定义了 ConstructorInterceptPoint、InstanceMethodsInterceptPoint、StaticMethodsInterceptPoint

    这三个 构造器,实例方法, 静态方法拦截点的 抽象 方法, 让插件子类去实现拦截点

     下面是在 Dubbo  为例 DubboInstrumentation  插件主体实现依赖的类图关系:

    上面的是以 ClassEnhancePluginDefine 为核心的主体的核心类图结构, 现在主要讲一下此类的内部的主要构成与执行序列, 完整的类图如下所示:

    从上面可知, ClassEnhancePluginDefine 继承至 AbstractClassEnhancePluginDefine , 做为所有的插件都要继承实现的基础, 抽象类AbstractClassEnhancePluginDefine 具体定义了什么行为, 这些行为又有哪些意义?什么地方在调用这个方法?

    AbstractClassEnhancePluginDefine 中有四个方法

    • enhance 抽象方法, 抽象类中无实现, 子类去实现,具体的增强的逻辑
    • enhanceClass 抽象方法, 抽象类中无实现,子类去实现,具体的要增强的类的 Match
    • witnessClasses 也是可以被重载的一个方法,子类可重载
    • define 实例方法,它主要做了下面的几次事 ( 入口方法)

    1 找到增强插件类的所有显式指定的 WitnessClasses - 用户自己重载显式指定
    2 调用 enhance 方法, 执行真正的插件的增强逻辑,返回新的 DynamicType.Builder
    3 设置上下文, 标记初始化定义步骤已完成
    4 最后再返回新的 第 2 中的增强后的 newClassBuilder

    那么在这个类中,最为重要的一个方法 define 又是谁调用的呢?

    上面的就是这个类方法被使用的 Usages 情况, 主要是被 Sky-Walking Agent 的 Agent 主入口调用, 这个方法也是使用 byte-buddy 的字节码增强的 agent 中的一个使用范式, 也就是说在 agent 执行真正的 transformer , 所有的插件中因为都是继承至AbstractClassEnhancePluginDefine , 自然它们的 define 的定义的初始化方法, 也就会被全部调用初始化, 代码如下所示:

     对于 snow-walking  来说, 使用 Agent  的固定范式( 来自于 byte-buddy  的固定实现方式)如下所示:

    至此,已经分析完了, 所有 sky-walking  的自定义插件都必须要继承并重载的抽象类 AbstractClassEnhancePluginDefine  , 它定义了所有的Agent 插件都必须要实现的行为, 以及它自身需要定义与初始化的行为逻辑。

    接下为, 真正的主角上场了, 它就是 ClassEnhancePluginDefine  ,  其类的依赖关系如下:

     

     下面看一下 类的本身的方法, 总共有 六 个方法:

     enhance  方法, 重载了父类抽象类 AbstractClassEnhancePluginDefine  的方法, 做为所有插件的基类, 它定义了 enhance  方法必须所具备的操作。此方法的内部主要做了两件事:

    • enhanceClass , 增强一个类,以拦截其静态方法
    • enhanceInstance ,增强一个类,以拦截其构造器 与 实例方法

    除了重载的方法外, 其它的三个方法, 分别提供了对应的拦截点:

     在实现自定义插件逻辑的时候, 要重载这三个方法中的实现, 以 Dubbo 插件拦截扩展为例, 在 DubboInstrumentation  中 就重载了 父类的getInstanceMethodsInterceptPoints方法, 以此来告诉父类中的共性 enhance  增强处理逻辑, 本插件的实例方法拦截点的一些元数据信, 区配的方法是什么,要增强的类是哪一个,是否要重载其方法参数,这些信息。

     上面这里, 在具体的 ClassEnhancePluginDefine  实现插件内部, 对于拦截器 intercepter  都是指定的是 字符串 String  类型, 那么父类中ClassEnhancePluginDefine 是如何转化,并在什么时机调用这个 intercepter 的实现类, 以执行拦截的真实的逻辑的呢?

     那上面的这里从用户自定义的插件中显式的指定了 intercepter Fullname了,到了ClassEnhancePluginDefine后,进行enhance时,会最终都落在 byte-buddy  的 builder. method (....).intercpet (MethodDelegation.xxxx) 

    这样的固定增强的编程范式当中去。在 sky-walking 的实现中, 它又将 intercepter 的名称, 根据不同的增强类型,传入了不同类型的具体的委托执行实例当中去执行了。在 sky-walking 中, 根据增强的分类类型,委托执行实例分为以下几种:

    它们分别是, 静态方法固定拦截委托实现器,实例方法固定拦截委托实现器,构造器固定拦截委托实现器, 它们只有一个主要的核心执行方法, 就是 intercept , 这个方法在执行时是与 byte-budy 的 MethodDelegation 配套使用的, 此方法必须要显式的按规定指定相应的byte-buddy 的 Annotation 方能正常的执行增强工作。

    因为这三个固定类型的拦截器的处理方式都是差不多, 这里就以 静态方法拦截器举例分析其内部的执行原理。先看一下这个拦截器的类
    的依赖情况:

    从上面的此拦截器的依赖情况可以, 它主要依赖两个:

    • StaticMethodsAroundInterceptor , 这个接口的实现者是自定义插件自行根据需求实现的
    • InterceptorInstanceLoader ,这个是用户自定义插件的拦截器的类加载器

    之前我们说, 执行 byte-buddy 固定的 intercept 逻辑范式时, 通过 MethodDelegation 委托给了 sky-walking 的预设定的几个类型的拦截器,在构建这些固定拦截器时, 传入的都是用户自定义的拦截器的 ClassFullName , 所以在真实的固定类型的拦截器内部,就得有一个机制去加载用户自定义的拦截器,只有这样, 这些拦截器才能被调用执行。
    这些拦截器有三个固定的方法:

    • beforeMethod , 被拦截方法之前执行
    • afterMethod ,被拦截方法之后执行
    • handleMethodException ,处理捕获执行期间的异常

    OK , 分析到这里, 基本上, 用户自定义增强插件,如何被增强,如何被拦截执行的过程应该算是比较清楚了。

    增强与拦截机制总结

    下图是整理后的关于 sky-walking  的 APM 中用户自定义插件的完整的调用链路, 它对于插件如何生效并进行增强与拦截的调用过程做了描绘。

  • 相关阅读:
    VisualSVN-Server windows 版安装时报错 "Service 'VisualSVN Server' failed to start. Please check VisualSVN Server log in Event Viewer for more details."
    Pytest 单元测试框架之初始化和清除环境
    Pytest 单元测试框架入门
    Python(email 邮件收发)
    Python(minidom 模块)
    Python(csv 模块)
    禅道简介
    2020年最好的WooCommerce主题
    Shopify网上开店教程(2020版)
    WooCommerce VS Magento 2020:哪个跨境电商自建站软件更好?
  • 原文地址:https://www.cnblogs.com/Java-Script/p/11518808.html
Copyright © 2011-2022 走看看