【SkyWalking】千亿级链路追踪实践-Agent优化改造(一)

一、开篇

公司从2021年开始在公司推广SkyWalking,在落地期间对开源SkyWalking也做了较多改造,为公司提供了全链路追踪能力,提升了大家排查问题、优化性能的效率。

1 规模

现有数据量已达数千亿,数据规模如下:

  • 每日存储50亿TraceSegment,约500亿span链路数据
  • 支持存储2月trace数据,共约3000亿TraceSegment,1.5万亿span链路数据
  • 链路数据每日4TB-6TB
  • 提供服务端、Native端、H5端的链路,有500+Service7000+JVM实例已接入

2 文章计划

拆分成系列文章,从Agent、OapServer(trace、metrics、topology等)、存储(ES)等方面介绍。

本文讲解SkyWalking的Agent。

我们是基于SkyWalking8.5.0版本升级改造,其他版本的架构、设计可能有所差异。

二、SkyWalking agent基本介绍

1 探针模型

Apache SkyWalking 的探针模型为分布式系统提供了自动、无侵入的追踪能力。

探针模型主要包含如下部分:

  • Java Agent 探针:利用 Java Agent 技术,通过静态加载的方式,对目标应用的字节码进行增强。
  • 插件化架构:微内核提供了基础的核心功能和通用服务,插件实现某些具体功能,如:某个特定框架或库的支持。

下面讲解一下Java Agent 探针和微内核 + 插件架构。

2 Java Agent 探针

2.1 Java Agent 探针和 SDK 方式探针

分布式追踪的探针,一般分为Java Agent 探针和 SDK 方式探针。

  • Java Agent 探针:利用 Java Agent 技术。例如 :SkyWalking、Pinpoint

  • SDK 方式探针:引入相应的 SDK 依赖。例如:Spring Cloud Sleuth、cat

关于Java Agent,可以参考笔者之前写的文章:【JVM】Java agent超详细知识梳理

2.2 Java Agent 探针的优势

SkyWalking的Java Agent 探针和常见的 SDK 方式探针对比,有如下优势:

  • 无侵入性:使用 Java Agent 探针,开发者无需修改应用的源代码引入SDK,通过 JVM 参数在应用启动时加载 Agent,实现字节码增强来采集追踪数据,所以Java Agent 探针的接入、升级,都是更便捷。
  • 自动追踪:SkyWalking agent 能够自动识别和追踪众多的流行框架和库,例如 HTTP 服务器、数据库调用和 RPC 框架,SkyWalking社区非常活跃,已提供了大部分主流的开源框架和库对应的链路插件。
  • 智能选择与框架或库版本匹配的插件 :以 ElasticSearch client为例,SkyWalking Agent 提供了如 elasticsearch-5.x-pluginelasticsearch-6.x-pluginelasticsearch-7.x-plugin 等多个插件进行适配、链路追踪。对于 5.x 版本的 ElasticSearch 客户端,仅 elasticsearch-5.x-plugin 会被激活,而其他版本的插件则保持不激活状态。而SDK方式探针,接入会麻烦一些,需要考虑引入的SDK依赖后,SDK版本是否能兼容现有的框架或库的版本。

3 插件化架构

SkyWalking Agent 采用了插件化架构(Plug-in Architecture)。

3.1 什么是插件化架构?

插件化架构也称为微内核架构(Microkernel Architecture),是一种面向功能进行拆分的可扩展性架构。

插件化架构中分为内核系统和插件模块

  • 内核系统:内核模块功能是比较稳定的,只负责管理插件的生命周期,不会因为系统功能的扩展而不断进行修改。

  • 插件模块:功能上的扩展全部封装到插件之中,插件模块是独立存在的模块,包含特定的功能,能拓展核心系统的功能。通常,不同的插件模块之间互相独立,当然,你可以设计成一个插件依赖于另外一个插件,但应尽量让插件之间的相互依赖关系降低到最小,避免繁杂的依赖带来扩展性问题。

应用场景

在基于产品的应用中通常会使用插件化架构,如工具类产品IDEA、Eclipse、Maven、Gradle ,众多功能都是以插件形式增加。

而日常用的开源框架、系统也是插件化架构:如Dubbo、ElasticSearch。

插件化架构的优点:

  • 降低测试成本:从软件工程视角,插件化架构分离了变动部分与稳定部分,降低了测试的成本,符合设计模式中的开放封闭原则。

  • 增强稳定性:每个插件都运行在相对独立的环境中,因此即便某个插件出现问题,也不会影响到核心系统和其他插件。

  • 优良的可扩展性:新增功能或业务时,仅需添加相应的插件;而下线旧功能只需移除对应插件。

3.2 SkyWalking的插件化架构设计

SkyWalking的Java Agent 探针,以apm-agent-core模块为内核系统,apm-sdk-plugin、bootstrap-plugins等模块为插件模块。

对应的插件化架构设计如下:

内核系统

以apm-agent-core模块为内核系统,在这个内核中,定义了一系列核心的接口、类和功能,如:

  • 服务注册:Agent 启动时,将自己代表的服务实例注册到 SkyWalking 后端,并定期发送心跳、上报元数据。
  • 插件管理:负责加载、初始化和管理所有的 SkyWalking 插件。
  • trace上下文管理:确保在请求的整个生命周期内,trace数据能在线程和进程间准确关联与传递。
  • 数据上报:将采集的trace数据、metrics数据等发送到 SkyWalking 的后端服务。
  • 配置管理: 解析和加载配置文件、环境变量,并支持从OapServer获取动态配置。
  • 日志管理:提供日志记录的功能。
  • 错误处理:确保在执行过程中遇到的任何错误都能被妥善处理,不会影响到目标应用的运行。
  • 性能剖析:提供对应用程序的性能剖析功能,即 Profile,进行动态的性能剖析,收集方法级别的执行信息。

插件模块

  1. apm-sdk-plugin功能:包含常见框架和库的自动探针插件。这是SkyWalking的核心插件库。 它包含了一系列专为各种流行框架和库(如Spring、JDBC和Elasticsearch)设计的插件。这些插件的主要功能是自动拦截和增强目标应用中的方法调用,从而实现对应用的无侵入式监控,捕获关键的跟踪数据。

    子模块:dubbo-plugin、mysql-8.x-plugin等

  2. bootstrap-plugins

    功能:捕获JVM启动早期行为的插件。 这些插件被设计用于Java Agent的bootstrap类加载器级别。其主要目的是拦截和增强Java的核心库,例如JDK自带的某些类。这种特殊的设计确保了在关键的、核心的场景中,跟踪可以被正确地执行,而不受应用自身或其他库的干扰。

    子模块:jdk-threading-plugin、jdk-threadpool-plugin等

  3. apm-toolkit-activation

    功能 :激活SkyWalking APM Toolkit库中提供的API。 SkyWalking APM Toolkit提供了一套API,允许开发者进行手动埋点,以满足某些自定义的监控需求。apm-toolkit-activation模块就是用来激活这些手动埋点功能的插件集。

    子模块:apm-toolkit-log4j-1.x-activation、apm-toolkit-trace-activation等

  4. optional-plugins

    功能:可选插件,为特定场景提供额外功能。 它们不会在默认配置中被启用,但为特定场景或不常见的框架提供了额外的支持。用户可以根据实际需求选择性地启用这些插件。

    子模块::gateway-3.x-plugin、trace-ignore-plugin、zookeeper-3.4.x-plugin等

  5. optional-reporter-plugins

    功能:可选的数据上报方式插件。 SkyWalking Agent不仅支持默认的gRPC方式向后端上报数据,还可以通过这些可选的上报插件选择其他传输协议或格式。

    子模块:optional-reporter-plugins等

三、agent改造-缩减agent编译、启动时间

1 缩减agent编译时间

agent编译打包是非常耗费时间,本地编译打包往往5到10分钟才能完成,且agent代码有改动必须完整编译打包再挂载才能调试,所以频繁执行漫长耗时的编译打包,对开发调试带来很大的不便。

于是,我们做了下面的这些优化,预估减少了**50%**的编译时间。

1.1 去除用不到的插件

在maven管理中去除用不到的agent插件,例如:baidu-brpc-plugincassandra-java-driver-3.x-plugin等十几个插件,代码量减少,加快编译速度。

1.2 使用maven并行编译

maven支持指定多线程并行编译,对应的参数是-T,对应使用方式如下:

  • mvn -T 4 表示用 4 个线程构建。
  • mvn -T 2C 表示每个CPU核分配 12个线程进行构建,8核则为16线程。

效果:不同机器配置,调整不同线程数。使用mvn -T命令,预计减少30%-50%的编译时间。

2 缩减agent启动时间

SkyWalking Agent作为Java Agent挂载到宿主应用JVM,agent自身启动时间往往需要20秒 以上,如果应用有大量实例要部署,如某个应用有100个实例,则至少增加了2000 秒的启动耗时,严重影响发布效率。

于是我们做了如下技术改造,优化启动耗时。

2.1 去除Kafka topic 的检查部分

我们使用了Kafka通道上报agent数据,可以去除下面的Kafka检查topic是否存在的逻辑,因为Kafka的topic会提前创建好,没有必要做检查。

效果:启动耗时减少了3-4秒。

2.2 过滤不需要增强的类

SkyWalking Agent通过Byte Buddy 增强匹配环节,会扫描匹配较多的类。SkyWalking Agent自身也会过滤一些不需要的类,其过滤逻辑在org.apache.skywalking.apm.agent.SkyWalkingAgent#premain方法的如下代码:

java 复制代码
        AgentBuilder agentBuilder = newAgentBuilder().ignore(
            nameStartsWith("net.bytebuddy.")
                .or(nameStartsWith("org.slf4j."))
                .or(nameStartsWith("org.groovy."))
                .or(nameContains("javassist"))
                .or(nameContains(".asm."))
                .or(nameContains(".reflectasm."))
                .or(nameStartsWith("sun.reflect"))
                .or(allSkyWalkingAgentExcludeToolkit())
                .or(ElementMatchers.isSynthetic()));

但是,SkyWalking过滤的类很有限,而一般应用自身的类有几百个以上,此外也会引入几十个JAR,这些类累计起来至少有几千个类。

而我们实际需要的插件总数不过几十个,需要增强处理的类其实也可能就几十、上百个。所以我们扩大过滤的范围,只匹配需要增强的类:

  • 移除未使用到的插件,避免不必要的匹配逻辑

  • com.alibaba开头类,只增强dubbo相关类MonitorFilter、druid相关类com.alibaba.druid.**

  • org.apache开头的类很多,只增强名字是xxx的几个类
  • 对于继承型匹配,尽量约束开发者实现类的全限定名的根。特别是当这个类是一个接口或抽象类,并且它有很多实现类或子类时,插件开发者应当尽量精确地指定这个类的全限定名。这样,SkyWalking只会增强那些真正需要的类,避免不必要的性能开销
  • 自身公司的package如果命名规则为com.company.xxx,且有自己构建trace的需求,则com.company.xxx不忽略

如何确定哪些类需要增强?

上面提到控制匹配逻辑,限定只匹配的需要增强的类。那如何确定哪些类需要增强?有下面的方式:

1.找到需要用到的插件,找到xxxInstrumentation插桩类,搜索需要增强的类,例如druid-1.x-plugin插件中,插桩类是org.apache.skywalking.apm.plugin.druid.v1.define.DruidDataSourceInstrumentation,对应的代码:

java 复制代码
public class DruidDataSourceInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
    private static final String ENHANCE_CLASS = "com.alibaba.druid.pool.DruidDataSource";
}

其中com.alibaba.druid.pool.DruidDataSource就是要增强的类。

2.将agent日志级别调整到debug,

properties 复制代码
logging.level=${SW_LOGGING_LEVEL:DEBUG}

应用接入agent后,运行一段时间,当agent采集了项目里框架、组件的链路后,再查找skywalking-api.log,搜索关键词enhance class {} by {} completely.,下面是一段日志示例:

bash 复制代码
DEBUG 2023-10-08 19:23:42.791 main AbstractClassEnhancePluginDefine : enhance class org.apache.catalina.core.StandardHostValve by org.apache.skywalking.apm.plugin.tomcat78x.define.TomcatInstrumentation completely. 

其中StandardHostValve是要增强的类,ControllerInstrumentation是插桩类。

四、小结

本篇先讲解Agent的探针模型,以及缩减agent编译、启动时间的升级改造。

由于篇幅原因,其他Agent改造部分:

  • dubbo参数采集

  • 灰度发布、快速卸载

  • agent改造-自研插件

  • 其他优化

这些内容将放到下一篇文章。

参考

前同事硕总写的文章:

笔者文章:

【JVM】Java agent超详细知识梳理

相关推荐
Tech Synapse6 分钟前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
.生产的驴7 分钟前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
微信-since8119222 分钟前
[ruby on rails] 安装docker
后端·docker·ruby on rails
丁总学Java34 分钟前
ARM 架构(Advanced RISC Machine)精简指令集计算机(Reduced Instruction Set Computer)
arm开发·架构
代码吐槽菌2 小时前
基于SSM的毕业论文管理系统【附源码】
java·开发语言·数据库·后端·ssm
ZOMI酱2 小时前
【AI系统】GPU 架构与 CUDA 关系
人工智能·架构
豌豆花下猫2 小时前
Python 潮流周刊#78:async/await 是糟糕的设计(摘要)
后端·python·ai
YMWM_2 小时前
第一章 Go语言简介
开发语言·后端·golang
码蜂窝编程官方3 小时前
【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现
java·vue.js·spring boot·后端·spring·旅游