本源码来自于skywalking-agent 8.9.0版本
本节主要讲解skywalking-agent的轻量级队列内核,该实现在datacarrier模块主要用于进行数据发送OAP服务端的实现,主要采用缓存批量异步发送的方式进行发送。
注:本篇文章主要是作为自己看书后的总结,内容有可能会存在一些个人理解上的偏差,如果有网友找出问题欢迎提出,感谢!!!如果我理解上的错误误导了您,在此表示抱歉!!!

文章目录

  • 工程结构
  • 定义拦截形式
    • 类增强的拦截形式
    • 构造器方法的拦截形式
    • 实例方法的拦截形式
  • 实现拦截形式的拦截器

本篇主要描述skywalking探针插件的工程结构,包括每个包在探针中的作用,以及包下每个类是如何设计的。

工程结构

探针插件结构主要包括两部分:定义拦截形式、实现拦截形式的拦截器。例如tomcat插件结构如下:

其中:TomcatInstrumentation、ApplicationDispatcherInstrumentation定义了拦截形式,TomcatExceptionInterceptor、TomcatInvokeInterceptor、ForwardInterceptor 为实现拦截形式的拦截器。

探针插件拦截方法的结构主要由两部分组成:匹配器定义、拦截器实现。当插件拦截的方法被匹配器匹配成功时就会执行探针插件的拦截器。

这里对于拦截形式类的写法官方建议为:*Instrumentation,对于拦截器官方建议为:*Interceptor。

定义拦截形式

对于使用字节码工具实现的 Skywalking Agent 来说,可以实现任意的拦截形式。这里主要介绍类增强的拦截定义和两种最常用的方法拦截形式定义。

类增强的拦截形式

定义类增强的拦截需要继承 AbstractClassEnhancePluginDefine,并重写 protected abstract ClassMatch enhanceClass();返回对象为ClassMatch,代表拦截类的匹配方式。这里常见的匹配方式如下:
#byName:通过类路径+类名进行匹配,不要使用.class.getName来获取类路径+类名,因为这样可能存在ClassLoader问题*。
#byClassAnnotationMatch:通过类注解,实现拦截类的匹配,注意这里不支持从父类继承的注解。
#byHierarchyMatch:通过父类或者接口实现拦截类的匹配。这里如果存在多实现可能存在多次拦截,所以不太建议使用这种方式,容易存在隐藏式BUG。

demo:

public abstract class AbstractControllerInstrumentation extends AbstractSpring4Instrumentation {@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[] {new ConstructorInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getConstructorMatcher() {return any();}@Overridepublic String getConstructorInterceptor() {return "org.apache.skywalking.apm.plugin.spring.mvc.v4.ControllerConstructorInterceptor";}}};}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[] {new DeclaredInstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.RequestMapping"));}@Overridepublic String getMethodsInterceptor() {return Constants.REQUEST_MAPPING_METHOD_INTERCEPTOR;}@Overridepublic boolean isOverrideArgs() {return false;}},new DeclaredInstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.GetMapping")).or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PostMapping"))).or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PutMapping"))).or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.DeleteMapping"))).or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PatchMapping")));}@Overridepublic String getMethodsInterceptor() {return Constants.REST_MAPPING_METHOD_INTERCEPTOR;}@Overridepublic boolean isOverrideArgs() {return false;}}};}@Overrideprotected ClassMatch enhanceClass() {return ClassAnnotationMatch.byClassAnnotationMatch(getEnhanceAnnotations());}protected abstract String[] getEnhanceAnnotations();}

构造器方法的拦截形式

定义构造器方法的拦截需要继承基类 AbstractClassEnhancePluginDefine ,并重写 public abstract ConstructorInterceptPoint[] getConstructorsInterceptPoints();

其中返回值 ConstructorInterceptPoint[] 的每个元素 ConstructorInterceptPoint 需要定义两个方法:getMethodsMatcher即为构造器方法的匹配方式;getMethodsInterceptor 即为针对该拦截的构造器需要执行的拦截器。

demo

public abstract class AbstractConnectionInstrumentation extends AbstractMysqlInstrumentation {@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[] {new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return named(Constants.PREPARE_STATEMENT_METHOD_NAME);}@Overridepublic String getMethodsInterceptor() {return org.apache.skywalking.apm.plugin.jdbc.mysql.Constants.CREATE_PREPARED_STATEMENT_INTERCEPTOR;}@Overridepublic boolean isOverrideArgs() {return false;}}};}@Overrideprotected abstract ClassMatch enhanceClass();
}

实例方法的拦截形式

定义实例方法的拦截,需要继承 ClassInstanceMethodsEnhancePluginDefine 类,并重写 public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
return null;
}
该方法需要定义3个方法
getMethodsMatcher:方法匹配的方式;
getMethodsInterceptor:对匹配的方法实现的增强类;
isOverrideArgs:对匹配的方法的入参是否支持修改。

demo

public abstract class AbstractConnectionInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {@Overridepublic ConstructorInterceptPoint[] getConstructorsInterceptPoints() {return new ConstructorInterceptPoint[0];}@Overridepublic InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {return new InstanceMethodsInterceptPoint[] {new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return named(Constants.PREPARE_STATEMENT_METHOD_NAME);}@Overridepublic String getMethodsInterceptor() {return org.apache.skywalking.apm.plugin.mssql.commons.Constants.CREATE_PREPARED_STATEMENT_INTERCEPTOR;}@Overridepublic boolean isOverrideArgs() {return false;}},new InstanceMethodsInterceptPoint() {@Overridepublic ElementMatcher<MethodDescription> getMethodsMatcher() {return named(Constants.PREPARE_CALL_METHOD_NAME);}@Overridepublic String getMethodsInterceptor() {return org.apache.skywalking.apm.plugin.mssql.commons.Constants.CREATE_CALLABLE_STATEMENT_INTERCEPTOR;}@Overridepublic boolean isOverrideArgs() {return false;}}}

实现拦截形式的拦截器

针对插件的拦截器类,给予开发者对所拦截的方法,在执行前、执行后、执行异常时,进行无侵入的拦截,通过调用skywalking agent相关核心开发库进行数据传递。

demo

public class TomcatInvokeInterceptor implements InstanceMethodsAroundInterceptor {private static boolean IS_SERVLET_GET_STATUS_METHOD_EXIST;private static final String SERVLET_RESPONSE_CLASS = "javax.servlet.http.HttpServletResponse";private static final String GET_STATUS_METHOD = "getStatus";static {IS_SERVLET_GET_STATUS_METHOD_EXIST = MethodUtil.isMethodExist(TomcatInvokeInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD);}/*** * The {@link TraceSegment#ref} of current trace segment will reference to the trace segment id of the previous* level if the serialized context is not null.** @param result change this result, if you want to truncate the method.*/@Overridepublic void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,MethodInterceptResult result) throws Throwable {Request request = (Request) allArguments[0];ContextCarrier contextCarrier = new ContextCarrier();CarrierItem next = contextCarrier.items();while (next.hasNext()) {next = next.next();next.setHeadValue(request.getHeader(next.getHeadKey()));}String operationName =  String.join(":", request.getMethod(), request.getRequestURI());AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);Tags.URL.set(span, request.getRequestURL().toString());Tags.HTTP.METHOD.set(span, request.getMethod());span.setComponent(ComponentsDefine.TOMCAT);SpanLayer.asHttp(span);if (TomcatPluginConfig.Plugin.Tomcat.COLLECT_HTTP_PARAMS) {collectHttpParam(request, span);}}@Overridepublic Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,Object ret) throws Throwable {Request request = (Request) allArguments[0];HttpServletResponse response = (HttpServletResponse) allArguments[1];AbstractSpan span = ContextManager.activeSpan();if (IS_SERVLET_GET_STATUS_METHOD_EXIST) {Tags.HTTP_RESPONSE_STATUS_CODE.set(span, response.getStatus());if (response.getStatus() >= 400) {span.errorOccurred();}}// Active HTTP parameter collection automatically in the profiling context.if (!TomcatPluginConfig.Plugin.Tomcat.COLLECT_HTTP_PARAMS && span.isProfiling()) {collectHttpParam(request, span);}ContextManager.getRuntimeContext().remove(Constants.FORWARD_REQUEST_FLAG);ContextManager.stopSpan();return ret;}@Overridepublic void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,Class<?>[] argumentsTypes, Throwable t) {AbstractSpan span = ContextManager.activeSpan();span.log(t);}private void collectHttpParam(Request request, AbstractSpan span) {final Map<String, String[]> parameterMap = new HashMap<>();final org.apache.coyote.Request coyoteRequest = request.getCoyoteRequest();final Parameters parameters = coyoteRequest.getParameters();for (final Enumeration<String> names = parameters.getParameterNames(); names.hasMoreElements(); ) {final String name = names.nextElement();parameterMap.put(name, parameters.getParameterValues(name));}if (!parameterMap.isEmpty()) {String tagValue = CollectionUtil.toString(parameterMap);tagValue = TomcatPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ?StringUtil.cut(tagValue, TomcatPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) :tagValue;Tags.HTTP.PARAMS.set(span, tagValue);}}
}

注意对于 beforeMethod 方法,如果想要修改入参,要在定义拦截器时将 isOverrideArgs 方法的返回值设置为 true,否则修改参数不会生效。

skywalking源码--探针插件工程结构相关推荐

  1. skywalking源码--探针插件开发

    本源码来自于skywalking-agent 8.9.0版本 本节主要讲解skywalking-agent的轻量级队列内核,该实现在datacarrier模块主要用于进行数据发送OAP服务端的实现,主 ...

  2. 链路追踪 SkyWalking 源码分析 —— Agent 插件体系

    点击上方"芋道源码",选择"设为星标" 做积极的人,而不是积极废人! 源码精品专栏 中文详细注释的开源项目 消息中间件 RocketMQ 源码解析 数据库中间件 ...

  3. skywalking 源码解析——多线程变量传递 EnhancedInstance

    大家好,我是烤鸭: 今天分享下 skywalking源码,正好自己用到相关的内容了. 1. 拦截点 三个主要的拦截器.构造方法.静态方法和示例方法,每个切面里都可以重写这些方法,并且指定进入的拦截器. ...

  4. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  5. 基于openfire源码开发插件

    [0]README 1)本文旨在 简述如何 基于openfire源码开发插件, 如何导入 openfire源码到 eclipse,参见 http://blog.csdn.net/pacosonswjt ...

  6. 易支付系统源码_刷脸支付系统源码,插件源码合作模式有哪些,采购源码需要注意什么...

    对刷脸支付比较关注的朋友,应该都知道源码.当拥有这个,就意味着有了独立的系统.也意味着可以打造自己的品牌,转化自己资源,获取更多的利润.但是想拥有一套源码也是不简单的,不仅因为编写的难度和价格,也因为 ...

  7. 漂亮的Emlog博客网站模板源码+附插件合集

    正文: 一款非常不错的博客源码,东西非常齐全,网站源码+模板+插件全部都有,还带有广告位功能以及各种人性化小插件,音乐播放插件等,有需要的自行去体验吧. 程序: wwesd.lanzoub.com/i ...

  8. SkyWalking 源码分析 —— Collector Storage 存储组件

    1. 概述 本文主要分享 SkyWalking Collector Storage 存储组件.顾名思义,负责将调用链路.应用.应用实例等等信息存储到存储器,例如,ES .H2 . 友情提示:建议先阅读 ...

  9. Skywalking源码分析【agent探针篇】

    Skywalking agent源码分析 字节码技术 入口方法 1.核心配置加载方式: 2.插件初始化: 3.插件(中间件or框架)的增强 增强点的寻找: 4.服务启动 5.插件体系 5.1.拦截实例 ...

最新文章

  1. 如何在mac终端上使用python3.5
  2. javaweb学习总结二十五(response对象的用法一)
  3. 湮灭尽头的一点光---炮粒子
  4. cinema 4d完全学习手册_PBL 行动者手册 Vol.2(幼儿版)发布,快来领取一本神奇的故事集!...
  5. 微型计算机,单片机和单板机是,9、微型计算机、单片机和单板机是()
  6. leetcode面试题 17.15. 最长单词
  7. python3.5 安装PyCrypto
  8. Web 安全与 Rational AppScan 入门
  9. 【elasticsearch】ES 单分片使用 From/Size 分页遇到重复数据
  10. javascript字符串的方法
  11. DFS CCPC2017 南宁I题
  12. 关于vue-cli中-webkit-flex-direction: column失效问题
  13. python视频网站项目_Python项目04 视频网站数据清洗整理和结论研究
  14. 3x3矩阵怎么求逆矩阵_矩阵型组织结构,郭士纳与任正非都是怎么说的?
  15. ASUS AURA无法启动问题
  16. 张健和他的Fcoin
  17. 全阶滑模观测器程序_滑模观测器设计
  18. 土木工程材料——混凝土
  19. 基于BP神经网络飞机颠簸预测
  20. LeetCode #378 JavaScript

热门文章

  1. 太平洋电信分享如何“构建敏捷网络,共筑网络安全防护带”
  2. 解决ZipEntry.getSize()返回-1的问题
  3. Pytorch:基于转置卷积解码的卷积自编码网络
  4. 最新H5网页分享到Twitter、Facebook带缩略图
  5. windows开机的问题
  6. canal同步mysql到es
  7. 说我菜?那好,我用Python制作电脑与手机游戏脚本来赢你
  8. protege的下载
  9. decode函数的作用
  10. NekoHTML 和 XPath