本源码来自于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. 嵌入式学习:存储器总结
  2. 用户未登录重定向到登录界面_Linux 用户登录记录
  3. FPGA 实验六 计数器、 ROM和DDS
  4. ES6新特性之Generator函数
  5. 【编程开发】Python---列表
  6. Leetcode--152. 乘积最大子数组(java)
  7. oracle result_cache_max_size,当设置RESULT_CACHE_MAX_SIZE参数并且重启过database后,Query Result Cache 还是被禁用的。...
  8. mybatis plus 动态创建表和字段_mybatis-plus maven代码生成器
  9. java构造函数的执行顺序,java构造函数和初始化函数的执行顺序
  10. Atitit paip.对象方法的实现原理与本质.txt
  11. 科蓝ichat泡分机器人 官网
  12. 使用URLDecoder对URL进行中文解析
  13. ubuntu安装nat123
  14. Houdini 笔记2
  15. 火热升级:360Safe VS 雅虎助手
  16. 漂亮有创意的思维导图模板下载教程,教你思维导图怎么画
  17. $ is not defined
  18. 微信小程序反编译 PC端
  19. C#绘制GPS星空图
  20. 一些与OWL相关的推理机的区别(如:Jess、Jena、Pellet等)

热门文章

  1. 从深度学习到LSTM
  2. Response总结
  3. PTA 7-274 魔镜
  4. 数据结构考研:电脑磁盘碎片的定义、产生原理和清理原因(文件碎片/机械硬盘/固态硬盘/硬盘清理/计算机/软件工程/王道论坛)
  5. (转)BT下载不死!Magnet(磁力链接)开创网络BT2.0时代!!
  6. Oracle 日期时间查询
  7. Linux 浏览器无法输入中文解决办法
  8. transitive dependencies (if any) will not be available, enable debug logging for more details
  9. IBM研究院院长:量子计算“大爆发”将在十年内到来
  10. java获取sqlserver连接并插入数据