从 0 到 1 精通 SkyWalking:分布式系统的 “透视镜“ 技术全解析

引言:当分布式系统遇见 "盲人摸象" 困境

在单体应用时代,我们排查问题就像在自己家里找东西,虽然偶尔也会找不到,但至少对整个空间了如指掌。而进入分布式系统时代,排查问题更像是在一个陌生的大型商场里找一串丢失的钥匙 ------ 你知道它就在某个地方,但完全不知道从何找起。

想象这样一个场景:用户投诉某个电商平台下单后长时间未收到确认信息。客服反馈给技术团队,前端工程师检查后发现接口调用超时,后端工程师查看服务日志未发现异常,数据库团队确认查询性能正常,运维团队表示服务器资源充足。每个环节看起来都没问题,但整个流程却失败了。这就是分布式系统的 "盲人摸象" 困境 ------ 每个团队只能看到自己负责的部分,无法把握全局。

SkyWalking 正是为解决这一困境而生的分布式追踪系统。它就像给分布式系统装上了 "透视镜" 和 "黑匣子",不仅能看到系统的整体运行状态,还能在出现问题时快速定位根因。本文将从理论到实践,全面解析 SkyWalking 的核心原理、架构设计、实战应用和高级特性,帮助你真正理解并掌握这一强大的分布式追踪工具。

一、SkyWalking 核心概念与价值定位

1.1 什么是 SkyWalking?

SkyWalking 是一个开源的分布式追踪系统,由华为工程师吴晟(现 Apache 基金会董事)发起,目前已成为 Apache 顶级项目。它主要用于微服务、云原生和容器化应用的可观测性分析,提供分布式追踪、服务网格(Service Mesh)遥测分析、度量聚合和可视化一体化解决方案。

简单来说,SkyWalking 能帮助我们回答这些关键问题:

  • 一个请求在分布式系统中经过了哪些服务?
  • 每个服务处理请求花费了多长时间?
  • 哪个服务或组件是系统性能瓶颈?
  • 当请求失败时,具体是在哪个环节出了问题?
  • 系统整体的调用拓扑结构是怎样的?

1.2 SkyWalking 的核心价值

在分布式系统中,SkyWalking 的核心价值体现在三个方面:

  1. 问题定位:快速定位跨服务调用中的异常和性能瓶颈,减少故障排查时间
  2. 性能优化:通过分析调用链和性能指标,发现系统优化点
  3. 系统可视化:将抽象的分布式系统转化为直观的拓扑图和指标图表
  4. 容量规划:基于历史数据和趋势分析,为系统扩容提供决策依据
  5. 服务治理:识别不健康的服务实例,为服务熔断和降级提供数据支持

1.3 可观测性三支柱与 SkyWalking 的定位

现代分布式系统的可观测性(Observability)建立在三个支柱上:

  • 追踪(Tracing):记录请求在分布式系统中的完整路径,关注单个请求的流转
  • 度量(Metrics):对系统状态的数值化描述,关注聚合数据和趋势
  • 日志(Logging):系统产生的离散事件记录,关注具体细节

SkyWalking 以分布式追踪为核心,同时整合了度量和日志功能,形成了一个完整的可观测性平台。与其他工具相比,SkyWalking 的特点是:

  • 探针性能优异,对应用侵入性小
  • 支持多种语言和框架
  • 提供丰富的可视化界面
  • 可扩展性强,支持自定义插件
  • 对 Service Mesh 有良好支持

1.4 SkyWalking 与其他可观测性工具的对比

工具 特点 优势场景 不足
SkyWalking 全栈可观测性,性能优秀,国产开源 微服务、云原生应用 生态相对 Prometheus+Grafana 较小
Zipkin 简单轻量,易于部署 中小规模分布式系统 功能相对简单,可视化较弱
Jaeger CNCF 毕业项目,与 K8s 生态融合好 Kubernetes 环境 对部分旧框架支持不够完善
Pinpoint 字节码增强,无需代码侵入 无法修改代码的场景 插件开发相对复杂
Prometheus+Grafana 度量监控强大,生态丰富 指标监控和告警 追踪功能需要额外集成

二、SkyWalking 核心原理深度解析

2.1 分布式追踪的基础:追踪上下文传播

分布式追踪的核心是如何将跨服务的请求关联起来,形成完整的调用链。SkyWalking 采用了与 OpenTelemetry 兼容的上下文传播机制,主要通过以下几个标识符实现:

  • Trace ID:整个分布式调用链的唯一标识,一个 Trace ID 代表一个完整的请求链路
  • Segment ID:每个服务处理请求时生成的片段 ID,一个 Trace 由多个 Segment 组成
  • Span ID:每个 Segment 中具体操作的标识,代表一个具体的处理单元

当请求从一个服务传递到另一个服务时,这些标识符通过网络协议(如 HTTP 头、RPC 元数据)进行传递,从而将分散在不同服务的处理过程关联起来。

2.2 SkyWalking 的核心工作流程

SkyWalking 的工作流程可以分为四个主要阶段:

  1. 数据采集:通过 Agent 探针对应用进行字节码增强,自动生成追踪数据;同时支持手动埋点补充关键业务信息
  2. 数据传输:Agent 采集的数据通过 gRPC 或 HTTP 协议发送到 SkyWalking 后端(OAP Server)
  3. 数据存储:OAP Server 对数据进行分析和聚合后,存储到指定的存储介质(如 Elasticsearch)
  4. 数据展示:通过 SkyWalking UI 展示系统拓扑、调用链、性能指标等,并提供告警功能

2.3 字节码增强技术:SkyWalking 的 "无侵入" 秘诀

SkyWalking Agent 之所以能实现对应用的无侵入式监控,核心在于其采用了字节码增强技术。简单来说,就是在应用程序的类加载过程中,动态修改字节码,插入监控逻辑,而无需修改应用源代码。

字节码增强的工作流程:

SkyWalking 内置了对主流框架的增强器,如 Spring Boot、Dubbo、MyBatis、HttpClient 等,覆盖了大多数常见的调用场景。对于自定义框架或特殊场景,也可以开发自定义增强器。

2.4 SkyWalking 的数据模型

SkyWalking 定义了一套完整的数据模型来描述分布式系统的运行状态,核心模型包括:

  1. 服务(Service):具有相同功能的一组实例的集合,如订单服务、用户服务
  2. 服务实例(Service Instance):服务的具体运行实例,通常对应一个进程
  3. 端点(Endpoint):服务中可以被调用的具体接口或方法,如 HTTP 接口、RPC 方法
  4. 进程(Process):操作系统层面的进程,一个服务实例对应一个进程
  5. 追踪(Trace):一个请求在分布式系统中的完整调用路径
  6. 片段(Segment):追踪在单个服务实例中的部分
  7. 跨度(Span):片段中的一个具体操作,如一次数据库查询、一次 HTTP 调用

这些模型之间的关系如下:

2.5 OAP Server 的核心功能

SkyWalking OAP(Observability Analysis Platform)服务器是整个系统的核心,负责数据处理、分析和存储,主要功能包括:

  1. 数据接收:接收来自 Agent 和其他数据源的数据
  2. 数据解析:解析不同格式的输入数据
  3. 拓扑分析:根据调用关系自动构建服务拓扑图
  4. 指标计算:聚合计算各种性能指标(响应时间、吞吐量、错误率等)
  5. 数据存储:将处理后的数据存储到指定的存储介质
  6. 查询服务:提供查询接口供 UI 和其他系统使用
  7. 告警管理:根据预设规则判断是否触发告警

OAP Server 采用了模块化设计,不同功能通过插件实现,便于扩展和定制。

三、SkyWalking 环境搭建与基础配置

3.1 环境准备

在开始搭建 SkyWalking 之前,需要准备以下环境:

  • JDK 17+(SkyWalking 9.x 及以上版本推荐)
  • 操作系统:Linux、Windows 或 MacOS
  • 存储介质:推荐使用 Elasticsearch 7.x 或 8.x
  • 网络:确保 Agent、OAP Server 和 UI 之间的网络连通

3.2 搭建 Elasticsearch 存储

SkyWalking 支持多种存储介质,其中 Elasticsearch 是最常用的选择,因为它在处理时序数据和支持复杂查询方面表现优异。

使用 Docker 快速启动 Elasticsearch 8.11.3(最新稳定版):

bash

复制代码
# 拉取镜像
docker pull elasticsearch:8.11.3

# 启动容器
docker run -d \
  --name elasticsearch \
  -p 9200:9200 \
  -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
  elasticsearch:8.11.3

验证 Elasticsearch 是否启动成功:

bash

复制代码
curl http://localhost:9200

成功启动会返回类似以下的响应:

json

复制代码
{
  "name" : "3f1a91f8b6a",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "3zH7x1a5Qv-5L7Z9y8YJ-Q",
  "version" : {
    "number" : "8.11.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "64cf052f3b56b10c041973289942cb37b108e6",
    "build_date" : "2023-12-08T11:33:53.634979450Z",
    "build_snapshot" : false,
    "lucene_version" : "9.8.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

3.3 安装 SkyWalking OAP Server

下载并解压 SkyWalking 9.7.0(最新稳定版):

bash

复制代码
# 下载
wget https://archive.apache.org/dist/skywalking/9.7.0/apache-skywalking-apm-9.7.0.tar.gz

# 解压
tar -zxvf apache-skywalking-apm-9.7.0.tar.gz
cd apache-skywalking-apm-bin

配置 OAP Server 使用 Elasticsearch 存储:

编辑config/application.yml文件,修改存储配置:

yaml

复制代码
storage:
  selector: ${SW_STORAGE:elasticsearch}
  elasticsearch:
    namespace: ${SW_NAMESPACE:""}
    clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200}
    protocol: ${SW_STORAGE_ES_HTTP_PROTOCOL:http}
    connectTimeout: ${SW_STORAGE_ES_CONNECT_TIMEOUT:3000}
    socketTimeout: ${SW_STORAGE_ES_SOCKET_TIMEOUT:30000}
    responseTimeout: ${SW_STORAGE_ES_RESPONSE_TIMEOUT:15000}
    numHttpClientThread: ${SW_STORAGE_ES_NUM_HTTP_CLIENT_THREAD:0}
    user: ${SW_ES_USER:""}
    password: ${SW_ES_PASSWORD:""}
    # 其他配置保持默认

启动 OAP Server:

bash

复制代码
# Linux/Mac
bin/oapService.sh

# Windows
bin/oapService.bat

OAP Server 启动成功后,会监听 11800 端口(gRPC)和 12800 端口(HTTP)。

3.4 启动 SkyWalking UI

SkyWalking UI 是一个基于 React 的前端应用,用于可视化展示监控数据。

启动 UI 服务:

bash

复制代码
# Linux/Mac
bin/webappService.sh

# Windows
bin/webappService.bat

默认情况下,UI 会监听 8080 端口,并连接本地的 OAP Server(127.0.0.1:12800)。如果 OAP Server 部署在其他地址,需要修改webapp/application.yml文件:

yaml

复制代码
server:
  port: 8080

spring:
  cloud:
    gateway:
      routes:
        - id: oap-route
          uri: http://localhost:12800  # 修改为实际的OAP Server地址
          predicates:
            - Path=/graphql/**,/ui/**,/api/**,/analysis/**,/logs/**

启动成功后,访问http://localhost:8080即可打开 SkyWalking UI 界面。

3.5 配置 SkyWalking Agent

SkyWalking Agent 是嵌入到应用中的探针,负责收集监控数据。

下载 Agent:

SkyWalking Agent 已经包含在之前下载的安装包中,位于agent目录下。也可以单独下载:

bash

复制代码
wget https://archive.apache.org/dist/skywalking/java-agent/8.16.0/apache-skywalking-java-agent-8.16.0.tgz
tar -zxvf apache-skywalking-java-agent-8.16.0.tgz

Agent 核心配置:

Agent 的主要配置文件是agent/config/agent.config,关键配置项包括:

properties

复制代码
# 应用名称(服务名称)
agent.service_name=${SW_AGENT_NAME:YourApplicationName}

# OAP Server地址
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:127.0.0.1:11800}

# 采样率,1表示100%采样
agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:-1}

# 日志级别
logging.level=${SW_LOGGING_LEVEL:INFO}

# 插件配置,默认启用所有插件
plugin.mount=${SW_MOUNT_PLUGINS:}

3.6 在 Spring Boot 应用中集成 Agent

创建一个简单的 Spring Boot 应用:

首先,创建一个 Maven 项目,pom.xml配置如下:

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>skywalking-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
        <lombok.version>1.18.30</lombok.version>
        <springdoc.version>2.1.0</springdoc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>${springdoc.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

创建一个简单的控制器:

java

复制代码
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Random;

/**
 * 演示控制器
 *
 * @author ken
 */
@Slf4j
@RestController
@RequestMapping("/demo")
@Tag(name = "演示接口", description = "用于SkyWalking演示的接口")
public class DemoController {

    private final RestTemplate restTemplate = new RestTemplate();
    private final Random random = new Random();

    /**
     * 简单的Hello接口
     *
     * @param name 名称
     * @return 问候语
     */
    @GetMapping("/hello/{name}")
    @Operation(summary = "Hello接口", description = "返回简单的问候语")
    public String hello(@PathVariable String name) {
        log.info("收到hello请求,名称:{}", name);
        
        // 模拟处理时间
        try {
            Thread.sleep(random.nextInt(200));
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("处理hello请求时发生中断", e);
        }
        
        return "Hello, " + name + "!";
    }

    /**
     * 调用另一个接口的示例
     *
     * @param id ID参数
     * @return 处理结果
     */
    @GetMapping("/call/{id}")
    @Operation(summary = "调用示例", description = "调用另一个接口的示例")
    public String callAnother(@PathVariable Long id) {
        log.info("收到call请求,ID:{}", id);
        
        // 模拟调用另一个服务
        String result = restTemplate.getForObject("http://localhost:8081/demo/process/" + id, String.class);
        
        return "处理结果:" + result;
    }
}

创建应用启动类:

java

复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 应用启动类
 *
 * @author ken
 */
@SpringBootApplication
public class SkywalkingDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkywalkingDemoApplication.class, args);
    }
}

使用 Agent 启动应用:

bash

复制代码
java -javaagent:/path/to/skywalking-agent/skywalking-agent.jar \
     -Dskywalking.agent.service_name=demo-service \
     -Dskywalking.collector.backend_service=localhost:11800 \
     -jar target/skywalking-demo-1.0.0.jar

其中:

  • -javaagent:指定 Agent 的路径
  • -Dskywalking.agent.service_name:设置服务名称
  • -Dskywalking.collector.backend_service:设置 OAP Server 地址

启动成功后,访问几次http://localhost:8080/demo/hello/world接口,然后打开 SkyWalking UI(http://localhost:8080),就可以看到服务的监控数据了。

四、SkyWalking 核心功能实战

4.1 服务拓扑图分析

服务拓扑图是 SkyWalking 最直观的功能之一,它能自动根据服务间的调用关系生成可视化的系统架构图。

查看服务拓扑图的步骤:

  1. 登录 SkyWalking UI
  2. 在左侧菜单中选择 "拓扑图"
  3. 选择时间范围(如最近 10 分钟)
  4. 点击 "刷新" 按钮

拓扑图会展示:

  • 所有被监控的服务节点
  • 服务之间的调用关系
  • 调用的吞吐量(每秒请求数)
  • 调用的响应时间
  • 调用的错误率

通过拓扑图,我们可以快速了解:

  • 系统由哪些服务组成
  • 服务之间的依赖关系
  • 哪些服务之间的交互频繁
  • 哪些调用路径存在性能问题(红色表示响应时间长或错误率高)

4.2 分布式调用链追踪

当系统出现问题时,调用链追踪是定位问题的关键工具。SkyWalking 可以展示一个请求从开始到结束的完整路径,包括每个环节的处理时间。

查询调用链的步骤:

  1. 在 SkyWalking UI 左侧菜单中选择 "追踪"
  2. 设置查询条件(如服务名称、时间范围、是否包含错误等)
  3. 点击 "查询" 按钮
  4. 在结果列表中选择一个追踪记录查看详情

调用链详情展示:

  • 每个服务的处理时间
  • 每个 Span 的具体信息(如 HTTP 方法、URL、参数摘要)
  • 异常信息(如果有)
  • 标签信息(如数据库语句、缓存键等)

示例:分析一个慢查询

假设我们发现/demo/call/{id}接口响应缓慢,通过调用链追踪发现:

  1. demo-service调用process-service/demo/process/{id}接口耗时 2.3 秒
  2. process-service中,ProcessService.process()方法耗时 2.2 秒
  3. 进一步查看发现,ProcessService中的数据库查询耗时 2.1 秒

通过这个调用链,我们可以快速定位到性能瓶颈在process-service的数据库查询上,而不是网络传输或其他环节。

4.3 性能指标监控

SkyWalking 提供了丰富的性能指标监控,帮助我们了解系统的运行状态。主要指标包括:

  • 响应时间(Response Time):服务处理请求的时间,包括 P50、P90、P99 等百分位值
  • 吞吐量(Throughput):单位时间内处理的请求数
  • 错误率(Error Rate):错误请求占总请求的比例
  • SLA(Service Level Agreement):服务可用性指标

查看性能指标的步骤:

  1. 在 SkyWalking UI 左侧菜单中选择 "仪表盘"
  2. 选择要查看的服务
  3. 选择时间范围
  4. 查看各项指标图表

通过性能指标,我们可以:

  • 发现服务的性能趋势
  • 比较不同服务的性能表现
  • 识别性能异常(如响应时间突增、错误率上升)
  • 评估系统的整体健康状况

4.4 日志集成与分析

SkyWalking 可以与应用日志集成,将日志与调用链关联起来,方便问题排查。

集成 Logback 日志框架:

  1. 添加 Logback 依赖:

xml

复制代码
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.8</version>
</dependency>
  1. 创建src/main/resources/logback-spring.xml配置文件:

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <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{36} - %msg%n</Pattern>
            </layout>
        </encoder>
    </appender>
    
    <!-- 输出到SkyWalking OAP的日志追加器 -->
    <appender name="SKYWALKING_LOG" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
        <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{36} - %msg%n</Pattern>
            </layout>
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="SKYWALKING_LOG" />
    </root>
</configuration>
  1. 添加 SkyWalking 日志工具包依赖:

xml

复制代码
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-logback-1.x</artifactId>
    <version>8.16.0</version>
</dependency>

配置完成后,应用日志中会自动包含TraceID,并且日志会被发送到 SkyWalking OAP Server。在 SkyWalking UI 的 "日志" 菜单中,可以根据TraceID查询与特定调用链相关的所有日志,实现日志与调用链的联动分析。

4.5 告警配置与使用

SkyWalking 支持基于各种指标设置告警规则,当指标超过阈值时会触发告警通知。

配置告警规则:

编辑 OAP Server 的config/alarm-settings.yml文件,添加或修改告警规则:

yaml

复制代码
rules:
  # 服务响应时间告警
  service_resp_time_rule:
    metrics-name: service_resp_time
    op: ">"
    threshold: 1000
    period: 10
    count: 3
    silence-period: 5
    message: "服务 {name} 的响应时间超过1000ms,持续3个周期"
  
  # 服务错误率告警
  service_error_rate_rule:
    metrics-name: service_error_rate
    op: ">"
    threshold: 0.05
    period: 10
    count: 2
    silence-period: 5
    message: "服务 {name} 的错误率超过5%,持续2个周期"
  
  # 端点响应时间告警
  endpoint_resp_time_rule:
    metrics-name: endpoint_resp_time
    op: ">"
    threshold: 500
    period: 10
    count: 3
    silence-period: 5
    message: "端点 {name} 的响应时间超过500ms,持续3个周期"

# 告警通知器配置
notification-settings:
  default:
    # 钉钉通知器
    dingtalk:
      enabled: true
      webhook: https://oapi.dingtalk.com/robot/send?access_token=your_token
      secret: your_secret
      message-type: TEXT
    # 邮件通知器
    email:
      enabled: false
      host: smtp.example.com
      port: 587
      username: alert@example.com
      password: password
      from: alert@example.com
      to: admin@example.com
      subject: "SkyWalking 告警通知"

配置说明:

  • metrics-name:要监控的指标名称
  • op:比较运算符(>、<、>=、<=、==)
  • threshold:阈值
  • period:检查周期(分钟)
  • count:连续多少次超过阈值后触发告警
  • silence-period:告警沉默期,避免重复告警(分钟)
  • message:告警消息内容

修改配置后需要重启 OAP Server 生效。在 SkyWalking UI 的 "告警" 菜单中,可以查看所有触发的告警记录。

五、SkyWalking 高级特性与定制化

5.1 自定义埋点与增强

虽然 SkyWalking 的自动埋点已经覆盖了大部分场景,但在某些情况下,我们需要添加自定义埋点来跟踪特定的业务逻辑。

使用 SkyWalking 工具包进行自定义埋点:

  1. 添加工具包依赖:

xml

复制代码
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>8.16.0</version>
</dependency>
  1. 在代码中添加自定义埋点:

java

复制代码
import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;
import org.apache.skywalking.apm.toolkit.trace.Trace;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.springframework.stereotype.Service;

/**
 * 自定义埋点示例服务
 *
 * @author ken
 */
@Service
public class CustomTraceService {

    /**
     * 使用@Trace注解标记需要追踪的方法
     *
     * @param orderId 订单ID
     * @return 处理结果
     */
    @Trace
    public String processOrder(Long orderId) {
        // 添加自定义标签
        ActiveSpan.tag("orderId", orderId.toString());
        
        try {
            // 模拟业务处理
            validateOrder(orderId);
            calculatePrice(orderId);
            saveOrder(orderId);
            
            // 记录日志,包含TraceID
            log.info("订单处理完成,订单ID:{},TraceID:{}", 
                    orderId, TraceContext.traceId());
            
            return "SUCCESS";
        } catch (Exception e) {
            // 标记Span为错误状态
            ActiveSpan.error(e);
            log.error("订单处理失败,订单ID:{}", orderId, e);
            return "FAIL";
        }
    }
    
    /**
     * 子方法也会被追踪
     */
    @Trace
    private void validateOrder(Long orderId) {
        // 模拟验证逻辑
        ActiveSpan.tag("step", "validate");
    }
    
    /**
     * 子方法也会被追踪
     */
    @Trace
    private void calculatePrice(Long orderId) {
        // 模拟计算价格
        ActiveSpan.tag("step", "calculate");
    }
    
    /**
     * 子方法也会被追踪
     */
    @Trace
    private void saveOrder(Long orderId) {
        // 模拟保存订单
        ActiveSpan.tag("step", "save");
    }
}

@Trace注解会让 SkyWalking 对方法进行追踪,创建相应的 Span。ActiveSpan类可以用来添加标签、记录错误等。通过自定义埋点,我们可以更细致地追踪业务流程,特别是核心业务逻辑的执行情况。

5.2 自定义指标与仪表盘

SkyWalking 允许我们定义自定义指标,并在 UI 中创建自定义仪表盘来展示这些指标。

创建自定义指标:

  1. 首先,在代码中使用 SkyWalking API 记录自定义指标:

java

复制代码
import org.apache.skywalking.apm.toolkit.meter.MeterFactory;
import org.apache.skywalking.apm.toolkit.meter.api.Counter;
import org.apache.skywalking.apm.toolkit.meter.api.Gauge;
import org.apache.skywalking.apm.toolkit.meter.api.Histogram;
import org.springframework.stereotype.Service;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定义指标服务
 *
 * @author ken
 */
@Service
public class CustomMetricService {

    // 订单创建计数器
    private final Counter orderCreateCounter = MeterFactory.counter("order.create.count")
            .tag("status", "success")
            .build();
            
    // 订单支付计数器
    private final Counter orderPayCounter = MeterFactory.counter("order.pay.count")
            .tag("status", "success")
            .build();
            
    // 库存 gauge 指标
    private final AtomicInteger inventory = new AtomicInteger(1000);
    private final Gauge inventoryGauge = MeterFactory.gauge("product.inventory", inventory::get)
            .tag("product", "phone")
            .build();
            
    // 订单金额直方图
    private final Histogram orderAmountHistogram = MeterFactory.histogram("order.amount")
            .tag("type", "normal")
            .build();

    /**
     * 记录订单创建
     */
    public void recordOrderCreate() {
        orderCreateCounter.increment();
    }
    
    /**
     * 记录订单支付
     */
    public void recordOrderPay() {
        orderPayCounter.increment();
    }
    
    /**
     * 更新库存
     */
    public void updateInventory(int amount) {
        inventory.addAndGet(amount);
    }
    
    /**
     * 记录订单金额
     */
    public void recordOrderAmount(double amount) {
        orderAmountHistogram.observe(amount);
    }
}
  1. 在业务代码中使用这些指标:

    @Service
    public class OrderService {

    复制代码
     @Autowired
     private CustomMetricService metricService;
     
     public void createOrder(Order order) {
         // 业务逻辑...
         
         // 记录指标
         metricService.recordOrderCreate();
         metricService.recordOrderAmount(order.getAmount());
         metricService.updateInventory(-order.getQuantity());
     }
     
     public void payOrder(Long orderId) {
         // 业务逻辑...
         
         // 记录指标
         metricService.recordOrderPay();
     }

    }

  2. 在 SkyWalking UI 中创建自定义仪表盘:

  • 登录 SkyWalking UI
  • 点击右上角的 "+" 号,选择 "创建仪表盘"
  • 输入仪表盘名称和描述
  • 点击 "添加图表",选择自定义指标
  • 配置图表类型、时间范围等
  • 保存仪表盘

通过自定义指标和仪表盘,我们可以监控业务层面的关键指标,如订单量、支付转化率、库存水平等,实现技术指标与业务指标的统一监控。

5.3 SkyWalking 插件开发

SkyWalking 的插件机制允许我们扩展其功能,支持更多的框架和组件。下面以开发一个简单的插件为例,介绍插件开发的基本流程。

插件开发步骤:

  1. 创建插件项目,pom.xml配置:

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>skywalking-custom-plugin</artifactId>
    <version>1.0.0</version>

    <properties>
        <skywalking.version>8.16.0</skywalking.version>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-agent-core</artifactId>
            <version>${skywalking.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-util</artifactId>
            <version>${skywalking.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- 被增强的目标框架依赖 -->
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>custom-framework</artifactId>
            <version>1.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  1. 创建增强类:

    import net.bytebuddy.description.method.MethodDescription;
    import net.bytebuddy.matcher.ElementMatcher;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
    import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
    import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
    import static net.bytebuddy.matcher.ElementMatchers.named;
    import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;

    /**

    • 自定义框架插件定义

    • @author ken
      */
      public class CustomFrameworkPlugin extends ClassInstanceMethodsEnhancePluginDefine {

      /**

      • 定义需要增强的类
        */
        @Override
        protected ClassMatch enhanceClass() {
        // 匹配com.example.framework.CustomService类
        return byName("com.example.framework.CustomService");
        }

      /**

      • 定义构造方法拦截点
        */
        @Override
        public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
        }

      /**

      • 定义实例方法拦截点
        */
        @Override
        public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[]{
        new InstanceMethodsInterceptPoint() {
        @Override
        public ElementMatcher<MethodDescription> getMethodsMatcher() {
        // 匹配execute方法
        return named("execute");
        }

        复制代码
             @Override
             public String getMethodsInterceptor() {
                 // 拦截器类
                 return "com.example.plugin.CustomServiceInterceptor";
             }
        
             @Override
             public boolean isOverrideArgs() {
                 return false;
             }
         }

        };
        }
        }

  2. 创建拦截器类:

    import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
    import org.apache.skywalking.apm.agent.core.context.ContextManager;
    import org.apache.skywalking.apm.agent.core.context.tag.Tags;
    import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
    import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
    import org.apache.skywalking.apm.agent.core.interceptor.enhance.EnhancedInstance;
    import org.apache.skywalking.apm.agent.core.interceptor.enhance.InstanceMethodsAroundInterceptor;
    import org.apache.skywalking.apm.agent.core.interceptor.enhance.MethodInterceptResult;
    import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;

    import java.lang.reflect.Method;

    /**

    • 自定义服务拦截器

    • @author ken
      */
      public class CustomServiceInterceptor implements InstanceMethodsAroundInterceptor {

      @Override
      public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
      Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
      // 创建一个新的Span
      AbstractSpan span = ContextManager.createLocalSpan("CustomService.execute()");

      复制代码
       // 设置组件类型
       span.setComponent(ComponentsDefine.CUSTOM);
       
       // 设置层级为中间件
       SpanLayer.asMiddleware(span);
       
       // 添加标签
       if (allArguments != null && allArguments.length > 0) {
           Tags.ARGS.set(span, String.valueOf(allArguments[0]));
       }

      }

      @Override
      public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
      Class<?>[] argumentsTypes, Object ret) throws Throwable {
      // 结束Span
      ContextManager.stopSpan();
      return ret;
      }

      @Override
      public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
      Class<?>[] argumentsTypes, Throwable t) {
      // 记录异常
      ContextManager.activeSpan().log(t);
      }
      }

  3. 创建插件描述文件:

src/main/resources目录下创建skywalking-plugin.def文件:

复制代码
custom-framework-plugin=com.example.plugin.CustomFrameworkPlugin
  1. 打包插件:

    mvn clean package

  2. 安装插件:

将生成的 jar 包复制到 SkyWalking Agent 的plugins目录下,重启应用即可生效。

通过开发自定义插件,我们可以扩展 SkyWalking 的监控能力,使其支持特定的框架、组件或业务逻辑,满足个性化的监控需求。

5.4 SkyWalking 与 Kubernetes 集成

在云原生环境中,SkyWalking 可以与 Kubernetes 无缝集成,实现对容器化应用的全面监控。

在 Kubernetes 中部署 SkyWalking:

  1. 创建命名空间:

yaml

复制代码
apiVersion: v1
kind: Namespace
metadata:
  name: skywalking
  1. 部署 Elasticsearch:

yaml

复制代码
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
  namespace: skywalking
spec:
  serviceName: elasticsearch
  replicas: 1
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: elasticsearch:8.11.3
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        env:
        - name: discovery.type
          value: single-node
        - name: xpack.security.enabled
          value: "false"
        - name: ES_JAVA_OPTS
          value: -Xms512m -Xmx512m
        resources:
          limits:
            cpu: 1
            memory: 1Gi
          requests:
            cpu: 500m
            memory: 512Mi
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
  namespace: skywalking
spec:
  selector:
    app: elasticsearch
  ports:
  - port: 9200
    targetPort: 9200
  clusterIP: None
  1. 部署 SkyWalking OAP Server:

yaml

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: skywalking-oap
  namespace: skywalking
spec:
  replicas: 1
  selector:
    matchLabels:
      app: skywalking-oap
  template:
    metadata:
      labels:
        app: skywalking-oap
    spec:
      containers:
      - name: skywalking-oap
        image: apache/skywalking-oap-server:9.7.0
        ports:
        - containerPort: 11800
          name: grpc
        - containerPort: 12800
          name: rest
        env:
        - name: SW_STORAGE
          value: elasticsearch
        - name: SW_STORAGE_ES_CLUSTER_NODES
          value: elasticsearch:9200
        - name: JAVA_OPTS
          value: -Xms512m -Xmx512m
---
apiVersion: v1
kind: Service
metadata:
  name: skywalking-oap
  namespace: skywalking
spec:
  selector:
    app: skywalking-oap
  ports:
  - port: 11800
    targetPort: 11800
    name: grpc
  - port: 12800
    targetPort: 12800
    name: rest
  1. 部署 SkyWalking UI:

yaml

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: skywalking-ui
  namespace: skywalking
spec:
  replicas: 1
  selector:
    matchLabels:
      app: skywalking-ui
  template:
    metadata:
      labels:
        app: skywalking-ui
    spec:
      containers:
      - name: skywalking-ui
        image: apache/skywalking-ui:9.7.0
        ports:
        - containerPort: 8080
        env:
        - name: SW_OAP_ADDRESS
          value: http://skywalking-oap:12800
---
apiVersion: v1
kind: Service
metadata:
  name: skywalking-ui
  namespace: skywalking
spec:
  selector:
    app: skywalking-ui
  ports:
  - port: 80
    targetPort: 8080
  type: NodePort
  1. 在 Kubernetes 中部署应用时集成 SkyWalking Agent:

yaml

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo-service
  template:
    metadata:
      labels:
        app: demo-service
    spec:
      initContainers:
      - name: init-agent
        image: busybox:1.35
        command:
        - sh
        - -c
        - wget -q -O /skywalking/agent.tar.gz https://archive.apache.org/dist/skywalking/java-agent/8.16.0/apache-skywalking-java-agent-8.16.0.tgz && tar -zxf /skywalking/agent.tar.gz -C /skywalking
        volumeMounts:
        - name: skywalking-agent
          mountPath: /skywalking
      containers:
      - name: demo-service
        image: demo-service:1.0.0
        ports:
        - containerPort: 8080
        env:
        - name: JAVA_OPTS
          value: >-
            -javaagent:/skywalking/agent/skywalking-agent.jar
            -Dskywalking.agent.service_name=demo-service
            -Dskywalking.collector.backend_service=skywalking-oap.skywalking:11800
        volumeMounts:
        - name: skywalking-agent
          mountPath: /skywalking
      volumes:
      - name: skywalking-agent
        emptyDir: {}

通过这种方式,我们可以在 Kubernetes 环境中实现 SkyWalking 的完整部署,并对容器化应用进行全面监控。SkyWalking 能够自动识别 Kubernetes 中的服务和实例,提供与容器平台深度集成的监控能力。

六、SkyWalking 性能优化与最佳实践

6.1 Agent 性能优化

SkyWalking Agent 作为嵌入在应用中的组件,其性能对整个系统有直接影响。以下是一些优化 Agent 性能的建议:

  1. 合理设置采样率

    • 高流量系统可以降低采样率(如 1%),减少数据量
    • 关键业务或调试期间可以提高采样率(如 100%)
    • 配置方式:agent.sample_n_per_3_secs=1(每 3 秒采样 1 个请求)
  2. 禁用不必要的插件

    • SkyWalking 默认启用所有插件,对于不需要的插件可以禁用
    • 配置方式:plugin.mount=!mysql,!redis(禁用 mysql 和 redis 插件)
  3. 调整缓存大小

    • 对于高并发系统,可以适当增大 Agent 的缓存
    • 配置方式:agent.buffer_size=30000(设置缓存大小为 30000)
  4. 异步报告数据

    • 确保 Agent 使用异步方式向 OAP Server 报告数据
    • 配置方式:agent.force_tls=false(禁用 TLS,提高传输效率)
  5. 合理设置批量发送参数

    • 调整批量发送的大小和间隔,平衡实时性和性能

    • 配置方式: plaintext

      复制代码
      agent.batch_size=1000
      agent.batch_interval=3000

6.2 OAP Server 性能优化

OAP Server 作为数据处理和分析的核心,其性能优化同样重要:

  1. 集群部署

    • 对于大规模系统,OAP Server 应采用集群部署,分担负载
    • 配置方式:通过cluster配置项设置集群模式(如 zookeeper、kubernetes)
  2. 调整 JVM 参数

    • 根据服务器配置合理分配内存,避免 OOM 或 GC 问题
    • 推荐配置:-Xms4g -Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=200
  3. 优化存储配置

    • 使用性能更好的存储介质(如 Elasticsearch 集群)
    • 调整索引生命周期管理,自动清理过期数据
    • 配置方式:在application.yml中设置storage.elasticsearch.indexTTL
  4. 水平扩展

    • 根据数据量和查询压力,增加 OAP Server 实例数量
    • 确保存储层也能相应扩展
  5. 优化线程池配置

    • 根据 CPU 核心数调整处理线程池大小
    • 配置方式:在application.yml中调整core.default.workers

6.3 存储优化

存储是 SkyWalking 性能的关键瓶颈之一,合理的存储优化可以显著提升系统性能:

  1. 选择合适的存储介质

    • 中小规模系统:可以使用 MySQL 或 TiDB
    • 大规模系统:推荐使用 Elasticsearch 集群
    • 超大规模系统:考虑使用专门的时序数据库(如 InfluxDB、TimescaleDB)
  2. Elasticsearch 优化

    • 增加数据节点数量,提高存储和查询性能
    • 合理设置分片和副本数量(推荐分片数 = 数据节点数)
    • 配置索引生命周期管理(ILM),自动删除过期数据
    • 调整刷新间隔(index.refresh_interval),平衡实时性和性能
  3. 数据保留策略

    • 根据业务需求设置不同数据的保留时间

    • 配置方式: yaml

      复制代码
      storage:
        elasticsearch:
          traceDataTTL: 7 # 追踪数据保留7天
          metricsDataTTL: 30 # 指标数据保留30天
          logDataTTL: 15 # 日志数据保留15天
  4. 定期优化

    • 定期执行存储优化操作(如 ES 的 force merge)
    • 监控存储性能指标,及时扩容

6.4 生产环境最佳实践

在生产环境中使用 SkyWalking,建议遵循以下最佳实践:

  1. 分环境部署

    • 开发、测试、生产环境使用独立的 SkyWalking 部署
    • 生产环境配置应更注重性能和稳定性
  2. 渐进式接入

    • 初次接入时,可以先选择非核心服务进行试点
    • 逐步扩大监控范围,观察系统性能影响
  3. 完善的监控告警

    • 不仅监控业务系统,也要监控 SkyWalking 自身
    • 设置关键指标的告警阈值,及时发现问题
  4. 安全配置

    • 生产环境应启用 TLS 加密传输
    • 限制 UI 和 API 的访问权限
    • 敏感数据脱敏处理
  5. 定期备份

    • 定期备份 SkyWalking 的配置和数据
    • 制定灾难恢复计划
  6. 持续优化

    • 定期 review 监控数据,优化系统性能
    • 关注 SkyWalking 新版本,及时升级获取新功能和性能优化
  7. 文档和培训

    • 建立 SkyWalking 使用文档
    • 对开发和运维人员进行培训,提高问题排查效率

七、总结与展望

SkyWalking 作为一款优秀的开源分布式追踪系统,为我们解决分布式系统的可观测性问题提供了全面的解决方案。从核心原理来看,它通过字节码增强技术实现了对应用的无侵入式监控,通过分布式追踪上下文传播将分散的服务调用关联起来,形成完整的调用链。

在功能上,SkyWalking 不仅提供了基础的服务拓扑图和调用链追踪,还整合了性能指标监控、日志分析和告警功能,形成了一个完整的可观测性平台。通过本文的介绍,我们了解了 SkyWalking 的核心概念、工作原理、环境搭建、功能使用以及高级特性。

从实践角度来看,SkyWalking 的优势在于:

  • 对应用的侵入性小,部署和使用简单
  • 性能优秀,对系统的影响小
  • 支持多种语言和框架,兼容性好
  • 提供丰富的可视化界面,易于理解和使用
  • 可扩展性强,支持自定义埋点、指标和插件

掌握 SkyWalking 不仅能帮助我们快速定位分布式系统中的问题,还能让我们更深入地理解系统的运行状态,为系统优化和架构演进提供数据支持。希望本文能成为你学习和使用 SkyWalking 的有益指南,让你在分布式系统的复杂世界中,拥有一双 "透视" 的眼睛。

相关推荐
历程里程碑18 小时前
二叉树---二叉树的中序遍历
java·大数据·开发语言·elasticsearch·链表·搜索引擎·lua
小信丶18 小时前
Spring Cloud Stream EnableBinding注解详解:定义、应用场景与示例代码
java·spring boot·后端·spring
Jinkxs18 小时前
SkyWalking - Spring Cloud Alibaba 全链路追踪实战
skywalking
无限进步_18 小时前
【C++】验证回文字符串:高效算法详解与优化
java·开发语言·c++·git·算法·github·visual studio
亚历克斯神18 小时前
Spring Cloud 2026 架构演进
java·spring·微服务
七夜zippoe18 小时前
Spring Cloud与Dubbo架构哲学对决
java·spring cloud·架构·dubbo·配置中心
海派程序猿18 小时前
Spring Cloud Config拉取配置过慢导致服务启动延迟的优化技巧
java
阿维的博客日记18 小时前
为什么不逃逸代表不需要锁,JIT会直接删掉锁
java
William Dawson18 小时前
CAS的底层实现
java
九英里路18 小时前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串