Skywalking的增强与拦截机制

Stella981
• 阅读 557

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

字节增强条件匹配

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

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

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

插件方式增强的扩展点

所有的基于 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 为例, 如果要自定义自己的插件实的话,则就要指定如下内容:

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

插件方式增强主体逻辑

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

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

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

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

AbstractClassEnhancePluginDefine 中有四个方法

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

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

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

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

Skywalking的增强与拦截机制

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

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

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

Skywalking的增强与拦截机制

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

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

Skywalking的增强与拦截机制

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

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

Skywalking的增强与拦截机制

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

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

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

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

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

增强与拦截机制总结

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

Skywalking的增强与拦截机制

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这