SkyWalking实战

1,318 阅读11分钟

前言

Skywalking是分布式系统的应用程序性能监视工具,专为微服务,云原生架构和基于容器(Docker,K8S,Mesos)架构而设计,它是一款优秀的APM(Application Performance Management)工具,包括了分布式追踪,性能指标分析和服务依赖分析等。

Skywalking是一个国产的开源框架,2015年有吴晟个人开源,2017年加入Apache孵化器,国人开源的产品,主要开发人员来自于华为,2019年4月17日Apache董事会批准SkyWalking成为顶级项目,支持Java、.Net、NodeJs等探针,数据存储支持Mysql、Elasticsearch等,跟Pinpoint一样采用字节码注入的方式实现代码的无侵入,探针采集数据粒度粗,但性能表现优秀,且对云原生支持,目前增长势头强劲,社区活跃。

本文主要介绍,在Spring Cloud微服务架构中,使用SkyWalking的经验,和调优案例等,如需快速入门SkyWalking,请参考:skywalking.apache.org/zh/2020-04-…

1、探针配置优化

1.1、过滤链路

  1. 过滤需要trace-ignore-plugin插件,该插件在optional-plugins目录下,是可选插件,所以我们需要复制到plugins目录下。
  2. 在agent/config目录下,创建apm-trace-ignore-plugin.config文件,文件内容如下:
   trace.ignore_path=${SW_AGENT_TRACE_IGNORE_PATH:/actuator,/actuator/**,/eureka/apps/,/eureka/**,Lettuce/**,Gson/**,Mysql/**,Redisson/**}

2、优化

2.1、集群

线上推荐搭建skywalking,步骤如下:

  • 第一步,搭建一个 Elasticsearch 服务的集群
  • 第二步,搭建一个注册中心的集群。目前 SkyWalking 支持 Zookeeper、Kubernetes、Consul、Nacos 作为注册中心。
  • 第三步,搭建一个 SkyWalking OAP 服务的集群,同时参考《SkyWalking 文档 —— 集群管理》,将 SkyWalking OAP 服务注册到注册中心上。
  • 第四步,启动一个 Spring Boot 应用,并配置 SkyWalking Agent。另外,在设置 SkyWaling Agent 的 SW_AGENT_COLLECTOR_BACKEND_SERVICES 地址时,需要设置多个 SkyWalking OAP 服务的地址数组。
  • 第五步,搭建一个 SkyWalking UI 服务的集群,同时使用 Nginx 进行负载均衡。另外,在设置 SkyWalking UI 的 collector.ribbon.listOfServers 地址时,也需要设置多个 SkyWalking OAP 服务的地址数组。

2.2、探针

2.2.1、版本选择

推荐使用最新版本的探针,即8.11.0,里面解决了一些bug。比如:修复 Spring Cloud Gateway 项目中无法通过 Skywalking 追踪 WebClient 调用的问题

2.2.2、插件

在agent/plugins有很多默认插件,其实有很多是我们用不到的。插件是可插拔的,删除后,在项目启动时,通过agent的logs可以看到就不会加载了。 image.png

2.3、数据清理机制

SkyWalking同时提供了数据清理机制,即我们采集的数据会保留多长时间。因为链路追踪这样的需求,大多数情况下只需要查找短时间内的数据,甚至是实时数据,那么我们就不需要长时间的保留采集到的数据。配置文件路径:path/config/application.yml。

core:
selector: ${SW_CORE:default}
default:
# Set a timeout on metrics data. After the timeout has expired, the metrics data will automatically be deleted.
enableDataKeeperExecutor: ${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true} # Turn it off then automatically metrics data delete will be close.
dataKeeperExecutePeriod: ${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5} # How often the data keeper executor runs periodically, unit is minute
recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:90} # Unit is minute
minuteMetricsDataTTL: ${SW_CORE_MINUTE_METRIC_DATA_TTL:90} # Unit is minute
hourMetricsDataTTL: ${SW_CORE_HOUR_METRIC_DATA_TTL:36} # Unit is hour
dayMetricsDataTTL: ${SW_CORE_DAY_METRIC_DATA_TTL:45} # Unit is day
monthMetricsDataTTL: ${SW_CORE_MONTH_METRIC_DATA_TTL:18} # Unit is month
enableDataKeeperExecutor:数据清理机制的开关,默认开启。
dataKeeperExecutePeriod:数据清理定时器间隔时间,默认5,单位分钟。
recordDataTTL:明细记录的有效期,默认90,单位分钟。
minuteMetricsDataTTL:分钟级指标数据有效期,默认90,单位分钟。
hourMetricsDataTTL:小时级指标数据有效期,默认36,单位小时。
dayMetricsDataTTL:天级指标数据有效期,默认45,单位天。
monthMetricsDataTTL:月级指标数据有效期,默认18,单位月。

但是在使用elasticsearch6.x存储采集数据时,是使用存储配置里面的有效期覆盖核心配置的有效期(elasticsearch7没有此配置,直接使用核心配置)。

storage:
selector: ${SW_STORAGE:h2}
elasticsearch:
# Those data TTL settings will override the same settings in core module.
recordDataTTL: ${SW_STORAGE_ES_RECORD_DATA_TTL:7} # Unit is day
otherMetricsDataTTL: ${SW_STORAGE_ES_OTHER_METRIC_DATA_TTL:45} # Unit is day
monthMetricsDataTTL: ${SW_STORAGE_ES_MONTH_METRIC_DATA_TTL:18} # Unit is month
recordDataTTL:记录明细有效期,默认7,单位天。
otherMetricsDataTTL:其他指标(分钟/小时/天)数据有效期,默认45,单位天。
monthMetricsDataTTL:月级指标数据有效期,默认18,单位月。

2.4、配置segment的span数量

在探针配置文件(config文件夹下的:agent.config文件)修改如下配置,默认是300:

agent.span_limit_per_segment=${SW_AGENT_SPAN_LIMIT:300}

配置文件配置后,探针在创建span的时候会判断是否超出限制,若已超出限制则不会将对应的span数据上报到skywalking服务端,在agent的日志里会报错,报错信息如下:

image.png

3、自定义链路追踪

  • 什么是TraceId

    • 用来标识一条请求链路,一条请求链路中包含一个Trace ID,多个Span ID
  • 背景

    • 对业务代码进行链路追踪,方便排查问题
    • 比如,某个接口请求耗时慢,想对业务接口方法进行追踪
    • controller->service 方法,记录业务方法加入到链路中,记录入参、返回值等
  • 编码

    • 添加依赖
    <dependency>
          <groupId>org.apache.skywalking</groupId>
          <artifactId>apm-toolkit-trace</artifactId>
          <version>8.5.0</version>
    </dependency>
    
    • 业务方法添加注解
    @Trace
    @Tags:记录入参和返回值,POJO记得重写toString方法
    
    • 举例
    @Trace
    @Tags({@Tag(key="detail",value="returnedObj"),
            @Tag(key="detail",value="arg[0]")})
    public Object detail(@RequestParam("id") Integer id) {
        //TODO
    }
    
    

4、无法通过gateway发现路由的服务链路

Spring Cloud Gateway 是基于 WebFlux 实现,必须搭配上apm-spring-cloud-gateway-2.1.x-plugin 和 apm-spring-webflux-x.x-plugin 两个插件,将agent/optional-plugins下的两个插件 复制到 agent/plugins目录下,再重新构建skywalking镜像,成功推镜像后,涉及的项目也要重新启动。

注意:其它项目不用加,如果加了,在探针的日志里可能会报错,报错了就收集不到数据了,只用加Gateway网关项目

5、数据模型

5.1、明细存储模型

5.1.1、注册模型

注册模型 (Inventory)需要继承RegisterSource类。 基类RegisterSource有 4个属性:

  1. sequence属性通过使用自动递增序列,来保证同一个注册模型中,注册模型的sequence是递增连续的,且保证全局唯一;
  2. registerTime属性用来记录注册模型的首次记录时间;
  3. heartbeatTime属性,心跳时间;
  4. lastUpdateTime属性,最后更新时间。

目前OAP有如下注册模型。

  1. Servicelnventory:服务名称注册模型, 记录着所有服务名称的定义,其中 包括Agent配置的service_name、 对端资源(peer)和RPC框架的调用地址的注册模型分析。
  2. Servicelnstancelnventory:服务实例注册模型, 以服务名称下的每一个服务进程为维度的注册模型。
  3. NetworkAddresslnventory:网络地址注册模型, 以IP+端口为维度的注册模型。
  4. EndpointInventory:Endpoint注册模型, 以端点名称(Endpoint)为维度的注册模型。

SkyWalking通过设计和使用各种维度的注册模型来优化网络传输效率和存储计算能力。

  1. 在探针启动时, 会根据本实例的进程信息和探针配置文件中的服务名称与OAP进行注册通信, 获取到本服务名称和服务实例的注册 sequence信息。在进程信息上报时,使用这些注册信息 可以优化网络传输效率。
  2. 在上报侧,在探针上报Trace Segment信息时, 端点名称会通过端点注册模型(Endpointlnventory) 将端点名称映射为端点名称的sequence, 对端资源会通过网络地址注册模型 (NetworkAddressInventory), 将对端资源的地址转换为网络地址注册模型中的sequence值, 从而在上报TraceSegment时,大幅减小 数据包的体积。
  3. 在流式计算侧,OAL脚本定义的索引 属性对已注册模型中的sequence属性进 行存储,实现存储优化, 同时注册模型信息会以JVM缓存的形式, 在OAP提供快速的sequence数据转义为全量的注册信息 。

下面以Servicelnventory (服务名称注册模型)为例, 来详细介绍每个注册模型结构中的共有属性, 以及此模型结构的独特属性字段的类型与描述,参见下表:

image.png

5.1.2、明细模型

明细模型(Record)需要继承Record类,它有Record有timeBucket两个属性,负责记录当前明细记录所在的时间窗口。

目前OAP有如下明细模型:

  1. SegmentRecord:Trace Segment明细记录模型,SkyWalking Agent发送过来的链路数据,经过skywalking-trace-receiver-plugin插件接收并解析后,得到的Trace Segment明细记录。
  2. AlarmRecord:报警明细数据模型,通过在OAP定义报警规则,在指标触发报警规则时,会产生对应的报警明细数据模型。报警指标的规则定义以及Webhook[配置详见](github.com/apache/skyw… nd/backend-alarm.md)。
  3. JaegerSpanRecord、 ZipkinSpanRecord:通过实现其他分布式数据的接收协议的插件,来完成其他分布式链路追踪Span明细记录。可在SkyWalking收集插件的目录下,学习相应的插件收集过程,插件的[URL](github.com/apache/skyw… receiver-plugin)。

下面以接收并解析SkyWalking Agent发过来的SegmentRecord,即Trace Segment明细记录模型数据为例,来详细介绍每个明细模型结构中的共有属性, 以及此模型结构的独特属性的字段类型与描述,参见表9-2。 image.png

6、日志打印skywalking的traceId

1、修改pom文件,增加依赖:

<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-logback-1.x</artifactId>
    <version>8.5.0</version>
</dependency>

2、修改logback.xml:

<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
    <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid][%thread] %-5level %logger{50} - %msg%n</pattern>
    </layout>
    <charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>

4、多线程场景下,拿不到tid

4.1、第一种办法

1、修改 skywalking-agent.jar所在目录的 config/agent.config文件,在里面修改/添加配置

#他将会去把指定包下所有`Runnable`的实现类给织入,可以前缀匹配
jdkthreading.threading_class_prefixes=com.chy

2、如果是下载官方编译好的插件,apm-jdk-threading-plugin.jar被放在了bootstrap-plugins文件夹中,移动到plugins即可。

参考文章

4.2、第二种办法

1、启动类中增加@EnableAsync

@EnableAsync
@SpringBootApplication
public class ManageApplication {
    //...
}

2、配置线程池

@Bean("generalExecutor")
public ThreadPoolTaskExecutor generalExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 设置核心线程数
    executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
    // 设置最大线程数
    executor.setMaxPoolSize(50);
    // 设置队列容量
    executor.setQueueCapacity(100);
    // 设置线程活跃时间(秒)
    executor.setKeepAliveSeconds(100);
    // 设置默认线程名称
    executor.setThreadNamePrefix("general-");
    // 设置拒绝策略
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 等待所有任务结束后再关闭线程池
    executor.setWaitForTasksToCompleteOnShutdown(true);
    return executor;
}

3、使用@Async注解

@Async("generalExecutor")
public void testThread() {
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("123");
}

4.3、第三种办法

1、引入依赖

<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>8.11.0</version>
</dependency>

2、使用依赖中的RunnableWrapper

CleanIpThreadPool.execute(RunnableWrapper.of(()->{
    try {
        TimeUnit.SECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.info("123");
}));

6、原理

6.1、oap的作用

在 Apache SkyWalking 中,OAP(OpenTelemetry Abstract Protocol)是一种用于接收、存储和查询追踪和性能监测数据的协议。OAP 主要用于实现 SkyWalking 的数据采集、传输和存储功能。

具体来说,OAP 扮演着以下几个关键的角色:

  1. 数据接收:OAP 接收来自 SkyWalking Agents 的性能和追踪数据。这些数据包括方法调用、跨服务调用、耗时统计等信息。通过 OAP,这些数据可以被集中汇总。
  2. 数据存储:OAP 将接收到的性能和追踪数据存储在后端的存储系统中,例如 Elasticsearch、H2、MySQL 等。这些数据的存储是为了后续的查询和分析。
  3. 数据查询:OAP 提供了查询接口,用于检索存储在后端的性能和追踪数据。这样,用户可以通过 SkyWalking 的 UI 或者 API 查询分布式系统的性能、调用链等信息。
  4. 数据处理:OAP 还负责对收集到的数据进行一些处理,例如聚合、过滤等,以便提供更高级的分析和监测功能。

总体而言,OAP 是 SkyWalking 数据平台的核心组件之一,它协调整个数据流的处理,确保数据从 Agents 收集到、经过存储、最终提供给用户查询和分析。通过 OAP,SkyWalking 提供了全面的分布式追踪和性能监测解决方案,帮助用户了解其分布式系统的运行状况、性能瓶颈和异常情况。

6.2、agent 如何探测的

Apache SkyWalking Agent 是一种用于在应用程序中收集性能和追踪数据的代理。它通过字节码注入技术实现,即在运行时修改 Java 字节码,以便收集有关应用程序性能和追踪信息的数据。

下面是 SkyWalking Agent 探测的基本原理:

  1. 字节码注入

    • SkyWalking Agent 利用字节码注入技术,在应用程序的字节码中插入代码片段。
    • 这些代码片段用于收集关于方法调用、HTTP请求、数据库访问等操作的性能和追踪数据。
  2. Agent 启动

    • 在应用程序启动时,SkyWalking Agent 会被加载到 JVM 中。

    • 这可以通过在应用程序启动脚本或者命令行参数中加入 SkyWalking Agent 的 Java Agent 参数实现。例如:

      javascriptCopy code
      -javaagent:/path/to/skywalking-agent.jar
      
  3. 探针(Instrumentation)

    • SkyWalking Agent 中包含了一组探针,每个探针负责收集特定类型的数据。
    • 这些探针通过字节码注入方式嵌入到目标类的方法中,以实现数据的收集。
  4. 数据采集

    • 探针在方法调用的开始和结束处插入代码,记录方法的执行时间和其他相关信息。
    • 在方法调用链上的不同节点,SkyWalking Agent 会收集相关的追踪信息,如调用者、被调用者、方法参数等。
  5. 数据上报

    • 收集到的性能和追踪数据会被上报到 SkyWalking Collector,这是一个中央化的组件,负责接收、存储和展示数据。
    • SkyWalking Agent 通过与 Collector 之间的通信,将数据发送给 Collector。

通过这种方式,SkyWalking Agent 实现了对应用程序性能和追踪数据的实时收集和监控。需要注意的是,字节码注入可能对应用程序的性能产生一定的影响,但 SkyWalking Agent 通常被设计为轻量级且低侵入的,以最小化对应用程序性能的影响。

6.2、traceId是如何生成的

Apache SkyWalking 是一个开源的分布式系统追踪和性能监控工具。在 SkyWalking 中,Trace ID 是通过一种称为 "SW6" 的上下文传递协议生成的。

SW6 协议的 Trace ID 由以下几个部分组成:

  1. TraceId(32位) :在整个分布式追踪链路中唯一标识一个请求的ID。TraceId是一个32位的数字,通常使用十六进制表示。
  2. SpanId(16位) :用于唯一标识一个 Span(追踪中的一个节点,表示一个服务的一次处理过程)。SpanId是一个16位的数字,通常使用十六进制表示。
  3. ParentSpanId(16位) :表示当前 Span 的父 Span 的ID。如果当前 Span 是整个 Trace 的根节点,则 ParentSpanId 为 0。
  4. Debug(8位) :用于标识 Trace 是否处于 Debug 模式。通常,Debug 模式用于开发和测试,以便更详细地追踪请求。

SW6 Trace ID 的格式如下:

scssCopy code
| TraceId (32 bits) | SpanId (16 bits) | ParentSpanId (16 bits) | Debug (8 bits) |

这个 Trace ID 会在整个请求链路中传递,每个服务都会将其解析并在处理请求时添加到日志中,以实现链路追踪。SW6 协议提供了一种轻量级的方式,以较小的开销在分布式系统中传递 Trace ID。

在 SkyWalking 中,你可以通过查看请求的头部或者相应的配置来获取 Trace ID。具体的实现可能会根据 SkyWalking 版本和语言有所不同,因此建议查阅相应的文档或源代码来获取详细信息。

7、其它