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 中用户自定义插件的完整的调用链路, 它对于插件如何生效并进行增强与拦截的调用过程做了描绘。

Skywalking的增强与拦截机制的更多相关文章
- Spring拦截机制之后端国际化心得
需求 前端请求的header里带有Prefer_Lang参数,向后端传递国际化信息,后端需要在处理业务之前(建立拦截机制),将Prefer_Lang保存于线程上下文. 思路分析 初次接收该需求时,为了 ...
- 讲讲Android事件拦截机制
简介 什么是触摸事件?顾名思义,触摸事件就是捕获触摸屏幕后产生的事件.当点击一个按钮时,通常会产生两个或者三个事件--按钮按下,这是事件一,如果滑动几下,这是事件二,当手抬起,这是事件三.所以在And ...
- Android 事件拦截机制一种粗鄙的解释
对于Android事件拦截机制,相信对于大多数Android初学者是一个抓耳挠腮难于理解的问题.其实理解这个问题并不困难. 首先,你的明白事件拦截机制到底是怎么一回事?这里说的事件拦截机制,指的是对触 ...
- 如何绕过chrome的弹窗拦截机制
如何绕过chrome的弹窗拦截机制 在chrome的安全机制里面,非用户触发的window.open方法,是会被拦截的.举个例子: var btn = $('#btn'); btn.click(fun ...
- Android开发系列之事件拦截机制
对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回fals ...
- Android事件拦截机制简单分析
前一阶段,在学习的时候,遇到了我觉得的我接触安卓以来的最多的一次事件拦截出来,那个项目,用到了slidemenu側滑菜单条,然后加上tab标签,还有轮播广告,listview上下滑动.viewpage ...
- 使用方法拦截机制在不修改原逻辑基础上为 spring MVC 工程添加 Redis 缓存
首先,相关文件:链接: https://pan.baidu.com/s/1H-D2M4RfXWnKzNLmsbqiQQ 密码: 5dzk 文件说明: redis-2.4.5-win32-win64.z ...
- 绕过chrome的弹窗拦截机制
在chrome的安全机制里面,非用户触发的window.open方法,是会被拦截的.举个例子: var btn = $('#btn'); btn.click(function () { //不会被拦截 ...
- View的事件拦截机制浅析
为什么要去分析view的事件 记得上周刚立的flag就是关于view的事件机制.那现在我来说说我对view的感受.关于view的事件,百度google一搜.一批又一批.但是能让人理解的少之又少.换句话 ...
随机推荐
- AtomicInteger原理
AtomicInteger的原理 java的并发原子包里面提供了很多可以进行原子操作的类,比如: AtomicInteger AtomicBoolean AtomicLong AtomicRefere ...
- 黑马vue---40、结合Node手写JSONP服务器剖析JSONP原理
黑马vue---40.结合Node手写JSONP服务器剖析JSONP原理 一.总结 一句话总结: 服务端可以返回js代码给script标签,那么标签会执行它,并且可带json字符串作为参数,这样就成功 ...
- Go 代码审查建议
https://github.com/golang/go/wiki/CodeReviewComments https://studygolang.com/articles/6054
- natively 在本地机器
For JavaScript-based projects, it makes sense to have a command line task runner that executes Java ...
- python3 @classmethod 和 @staticmethod 的区别
如果您将某个东西定义为classmethod,这可能是因为您打算从类而不是类实例中调用它. 定义类方法的几种方式: 常规方式 : 需要self隐士传递当前类 ...
- jenkins打完包在哪里
查看控制台输出最后面一行是jar包的位置 [JENKINS] Archiving /var/lib/jenkins/jobs/moven-test/workspace/target/projectA- ...
- Core Data概述(转)
Core Data是一个模型层的技术.Core Data帮助你建立代表程序状态的模型层.Core Data也是一种持久化技术,它能将模型对象的状态持久化到磁盘,但它最重要的特点是:Core Data不 ...
- Linux中移动,复制,删除,打包排除某个目录或文件
移动,复制,删除排除某个文件或目录 cp !(file1|dir2) /data/ 复制文件到/data/,排除file1和dir2 mv !(file1|dir2) /data/ 移动文件到/dat ...
- 【并行计算与CUDA开发】基于NVIDIA显卡的硬编解码的一点心得 (完结)
原文:基于NVIDIA显卡的硬编解码的一点心得 (完结) 1.硬解码软编码方法:大体流程,先用ffmpeg来读取视频文件的包,接着开启两个线程,一个用于硬解码,一个用于软编码,然后将读取的包传给解码器 ...
- kali PIN码破解
airmon-ng start wlan0 //开启网卡airodump-ng wlan0mon //监听模式,查找开启wps的apreaver -i wlan0mon -b [ap’s m ...