【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超详细知识梳理

相关推荐
Bs_MoneyMagnet2 小时前
基于springboot+vue的滑雪场票务与装备租赁系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计
Shota Kishi5 小时前
Solana Shreds 的 UDP 直投架构:Shredstream UDP Forwarding 的设计与实现
网络协议·架构·udp·区块链·solana
AlienZHOU6 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
狗头大军之江苏分军8 小时前
《潮水漫过十七岁》开学了
后端
苏三说技术8 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong9 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7049 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc9 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae10 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
行百里er11 小时前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring cloud·监控