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

字节增强条件匹配

在 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的增强与拦截机制的更多相关文章

  1. Spring拦截机制之后端国际化心得

    需求 前端请求的header里带有Prefer_Lang参数,向后端传递国际化信息,后端需要在处理业务之前(建立拦截机制),将Prefer_Lang保存于线程上下文. 思路分析 初次接收该需求时,为了 ...

  2. 讲讲Android事件拦截机制

    简介 什么是触摸事件?顾名思义,触摸事件就是捕获触摸屏幕后产生的事件.当点击一个按钮时,通常会产生两个或者三个事件--按钮按下,这是事件一,如果滑动几下,这是事件二,当手抬起,这是事件三.所以在And ...

  3. Android 事件拦截机制一种粗鄙的解释

    对于Android事件拦截机制,相信对于大多数Android初学者是一个抓耳挠腮难于理解的问题.其实理解这个问题并不困难. 首先,你的明白事件拦截机制到底是怎么一回事?这里说的事件拦截机制,指的是对触 ...

  4. 如何绕过chrome的弹窗拦截机制

    如何绕过chrome的弹窗拦截机制 在chrome的安全机制里面,非用户触发的window.open方法,是会被拦截的.举个例子: var btn = $('#btn'); btn.click(fun ...

  5. Android开发系列之事件拦截机制

    对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回fals ...

  6. Android事件拦截机制简单分析

    前一阶段,在学习的时候,遇到了我觉得的我接触安卓以来的最多的一次事件拦截出来,那个项目,用到了slidemenu側滑菜单条,然后加上tab标签,还有轮播广告,listview上下滑动.viewpage ...

  7. 使用方法拦截机制在不修改原逻辑基础上为 spring MVC 工程添加 Redis 缓存

    首先,相关文件:链接: https://pan.baidu.com/s/1H-D2M4RfXWnKzNLmsbqiQQ 密码: 5dzk 文件说明: redis-2.4.5-win32-win64.z ...

  8. 绕过chrome的弹窗拦截机制

    在chrome的安全机制里面,非用户触发的window.open方法,是会被拦截的.举个例子: var btn = $('#btn'); btn.click(function () { //不会被拦截 ...

  9. View的事件拦截机制浅析

    为什么要去分析view的事件 记得上周刚立的flag就是关于view的事件机制.那现在我来说说我对view的感受.关于view的事件,百度google一搜.一批又一批.但是能让人理解的少之又少.换句话 ...

随机推荐

  1. ORM 数据库使用

    使用 Flask-SQLAlchemy  来操作数据库 1 配置 本文使用sqlite来作为例子演示,在config.py里面更新下数据库的配置 import os basedir = os.path ...

  2. 微信小程序之--(与唯品会来场粉红色的邂逅 ???)

    Welcome to miaomiaoXiong's segmentfault 微信小程序之--(与唯品会来场粉红色的邂逅 ???) 买买买,虽然双十二刚过,可是唯品会的折扣却是依然火爆.一打开页面, ...

  3. iOS App的几种安全防范

    虽然公司的项目目前还不算健壮,安全问题对于大部分小公司来说似乎并没什么必要,不过要攻击的话,我有十足的把握,我们是无法承受冲击的.嘿嘿嘿~不过带着一颗入坑iOS的心思,搜集了一下资料后,还是做了一些尝 ...

  4. Angular4.x+Ionic3 踩坑之路之打包时出现JAVASCRIPT HEAP OUT OF MEMORY的几种解决办法

    最近开发的一个比较大型的App时打生产环境包是出现内存不足导致打包失败的问题.然后百度发现解决方法都是一样,自己试了一下都没什么暖用,心里只想用呵呵来形容了.最后经朋友介绍,技术问题还得去谷歌,git ...

  5. Linux学习笔记:fuser和lsof

    fuser 和 lsof 可以用于系统安全检查.用fuser查看哪些用户和进程在某些地方作什么:fuser -cu /root 简略显示fuser -muv /mnt3 分列显示 lsof 拥有更多的 ...

  6. SQLW3School-高级:SQL TOP 子句

    ylbtech-SQLW3School-高级:SQL TOP 子句 1.返回顶部 1. TOP 子句 TOP 子句用于规定要返回的记录的数目. 对于拥有数千条记录的大型表来说,TOP 子句是非常有用的 ...

  7. 用VLC读取摄像头产生RTSP流,DSS主动取流转发(一)

    用VLC读取摄像头产生RTSP流,DSS主动取流转发(一) 摄像机地址是192.1.101.51,VLC运行在192.1.101.77上,DSS服务器架设在192.1.101.30上. Step1:V ...

  8. 【世界之大,我用Python】Ubuntu 自动删除自带软件

    起因 Ubuntu是用来做开发程序的系统,如果存在大量的"垃圾"软件就不好了吧,不仅影响系统性能而且还会经常报错,所以每次安装完系统都会清理一次系统. 操作 我清理系统一般都是直接 ...

  9. 终端复用器tmux基础使用

    简介对于经常操作Linux系统的同学来说,远程连接Linux服务器使用最多的工具是SecureCRT或者Xshell,而将此时的环境称为终端环境.Tmux是两个单词的缩写,即“Terminal Mul ...

  10. 在先电IAAS平台中,搭建先电bigdata平台

    以两台节点为例来组件Hadoop分布式集群,这里采用的系统版本为Centos7,如下表所示: 主机名 内存 硬盘 IP地址 角色 master 8192MB 100G 192.168.200.131 ...