Skip to content
清晨的一缕阳光
返回

SkyWalking 链路追踪实战

SkyWalking 链路追踪实战

链路追踪原理

核心概念

Trace(追踪)

Span(跨度)

Segment(段)

Log(日志)

追踪流程

客户端请求


┌─────────────────┐
│   Gateway       │  ← 创建 Entry Span
│   Segment 1     │
└───────┬─────────┘


┌─────────────────┐
│   User Service  │  ← 创建 Entry Span + Exit Span
│   Segment 2     │
└───────┬─────────┘


┌─────────────────┐
│   Database      │  ← 创建 Entry Span
│   Segment 3     │
└─────────────────┘

数据模型

{
  "traceId": "TID-123456789",
  "segments": [
    {
      "segmentId": "SID-1",
      "spans": [
        {
          "operationName": "/api/users/1",
          "startTime": 1680000000000,
          "endTime": 1680000000100,
          "spanType": "Entry",
          "tags": [
            {"key": "http.method", "value": "GET"},
            {"key": "http.url", "value": "/api/users/1"}
          ],
          "logs": [
            {
              "time": 1680000000050,
              "data": [
                {"key": "event", "value": "processing"}
              ]
            }
          ]
        }
      ]
    }
  ]
}

Agent 配置

1. 基础配置

# agent.config

# 服务名
agent.service_name=user-service

# 命名空间(环境隔离)
agent.namespace=prod

# 实例名
agent.instance_name=instance-1

# OAP 服务器地址
collector.backend_service=127.0.0.1:11800

# 采样率(每秒采样数)
sample_n_per_3_secs=100

# 忽略的后缀
agent.ignore_suffix=.css,.js,.html,.png,.jpg,.jpeg,.gif,.ico,.svg

# 日志配置
logging.dir=logs
logging.level=DEBUG

2. JVM 启动参数

java -javaagent:/path/to/skywalking-agent.jar \
     -Dskywalking.agent.service_name=user-service \
     -Dskywalking.agent.namespace=prod \
     -Dskywalking.collector.backend_service=oap:11800 \
     -Dskywalking.logging.level=INFO \
     -jar app.jar

3. Docker 部署

FROM openjdk:17-jdk-slim

# 复制 Agent
COPY skywalking-agent /opt/skywalking-agent

# 设置环境变量
ENV SW_AGENT_NAME=user-service
ENV SW_AGENT_NAMESPACE=prod
ENV SW_AGENT_COLLECTOR_BACKEND=oap:11800

# 启动应用
ENTRYPOINT ["java", \
  "-javaagent:/opt/skywalking-agent/skywalking-agent.jar", \
  "-jar", "/app.jar"]

4. K8s 部署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  template:
    spec:
      containers:
        - name: user-service
          image: user-service:latest
          env:
            - name: JAVA_TOOL_OPTIONS
              value: "-javaagent:/opt/skywalking-agent/skywalking-agent.jar"
            - name: SW_AGENT_NAME
              value: "user-service"
            - name: SW_AGENT_NAMESPACE
              value: "prod"
            - name: SW_AGENT_COLLECTOR_BACKEND
              value: "skywalking-oap:11800"
          volumeMounts:
            - name: skywalking-agent
              mountPath: /opt/skywalking-agent
      volumes:
        - name: skywalking-agent
          configMap:
            name: skywalking-agent

自动追踪

1. Web 框架

SkyWalking 自动支持以下 Web 框架:

示例

@RestController
@RequestMapping("/users")
public class UserController {
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        // 自动追踪
        return userService.findById(id);
    }
}

2. RPC 框架

支持的 RPC 框架:

示例

@Service
public class UserServiceImpl implements UserService {
    
    @Override
    public User findById(Long id) {
        // 自动追踪
        return userRepository.findById(id);
    }
}

3. 数据库

支持的数据库:

示例

@Repository
public class UserRepository {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        // 自动追踪 SQL 执行
        return jdbcTemplate.queryForObject(
            "SELECT * FROM user WHERE id = ?",
            new Object[]{id},
            new UserMapper()
        );
    }
}

4. 消息队列

支持的消息队列:

示例

@Service
public class MessageService {
    
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    
    public void sendMessage(String topic, String message) {
        // 自动追踪消息发送
        kafkaTemplate.send(topic, message);
    }
    
    @KafkaListener(topics = "user-topic")
    public void listen(String message) {
        // 自动追踪消息消费
        processMessage(message);
    }
}

5. HTTP 客户端

支持的 HTTP 客户端:

示例

@Service
public class OrderService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    public Order createOrder(Order order) {
        // 自动追踪 HTTP 调用
        String url = "http://order-service/orders";
        return restTemplate.postForObject(url, order, Order.class);
    }
}

手动埋点

1. 使用注解

@Trace 注解

@Service
public class UserService {
    
    @Trace
    public User findById(Long id) {
        return userRepository.findById(id);
    }
    
    @Trace(operationName = "customOperation")
    public User create(User user) {
        return userRepository.save(user);
    }
}

@Tag 注解(添加标签)

@Service
public class UserService {
    
    @Trace
    @Tag(key = "userId", value = "arg[0]")
    @Tag(key = "result", value = "returnedObj")
    public User findById(Long id) {
        return userRepository.findById(id);
    }
    
    @Trace
    @Tag(key = "user", value = "arg[0]")
    @Tag(key = "id", value = "returnedObj.id")
    public Long create(User user) {
        return userRepository.save(user).getId();
    }
}

@NewSpan 注解(创建新 Span)

@Service
public class OrderService {
    
    @Trace
    public Order createOrder(Order order) {
        // 主流程
        validateOrder(order);
        
        // 创建新 Span
        saveOrder(order);
        
        return order;
    }
    
    @NewSpan("validateOrder")
    private void validateOrder(Order order) {
        // 验证逻辑
    }
    
    @NewSpan("saveOrder")
    private void saveOrder(Order order) {
        // 保存逻辑
    }
}

2. 使用 API

基础 API

@Service
public class UserService {
    
    @Autowired
    private Tracer tracer;
    
    public User findById(Long id) {
        Span span = Tracer.createSpan("findById");
        try {
            span.tag("userId", id.toString());
            span.log("开始查询用户");
            
            User user = userRepository.findById(id);
            
            span.log("查询完成");
            return user;
        } catch (Exception e) {
            span.errorOccurred();
            span.log("查询失败:" + e.getMessage());
            throw e;
        } finally {
            Tracer.stopSpan(span);
        }
    }
}

异步追踪

@Service
public class AsyncService {
    
    public CompletableFuture<String> asyncTask() {
        Span span = Tracer.createSpan("asyncTask");
        try {
            return CompletableFuture.supplyAsync(() -> {
                try (Scope ignored = Tracer.activateSpan(span)) {
                    span.log("执行异步任务");
                    return doWork();
                }
            });
        } finally {
            Tracer.stopSpan(span);
        }
    }
    
    private String doWork() {
        // 异步任务逻辑
        return "result";
    }
}

跨线程追踪

@Service
public class ThreadService {
    
    public void processInNewThread(Runnable task) {
        Span span = Tracer.createSpan("parentTask");
        try {
            // 捕获当前 Span
            Runnable wrappedTask = RunnableManager.wrap(task);
            
            new Thread(wrappedTask).start();
        } finally {
            Tracer.stopSpan(span);
        }
    }
}

3. 自定义标签

@Service
public class CustomTagService {
    
    @Trace
    @Tags({
        @Tag(key = "http.method", value = "arg[1].method"),
        @Tag(key = "http.url", value = "arg[1].url"),
        @Tag(key = "db.statement", value = "arg[0]"),
        @Tag(key = "user.id", value = "returnedObj.id")
    })
    public Result execute(String sql, Request request) {
        // 业务逻辑
        return result;
    }
}

链路分析

1. 查看追踪详情

在 SkyWalking UI 的”追踪”页面:

2. 性能分析

慢调用分析

错误分析

3. 拓扑分析

服务拓扑图

实例拓扑图

性能优化

1. 采样率配置

# 每秒采样数
sample_n_per_3_secs=100

# 或按比例采样
sample_rate=0.1  # 10% 采样率

2. 插件优化

禁用不需要的插件

# 禁用的插件
agent.exclude_plugins=skywalking-dubbo-plugin,skywalking-grpc-plugin

只启用需要的插件

# 启用的插件
agent.include_plugins=skywalking-springmvc-plugin,skywalking-mysql-plugin

3. 异步日志

# 异步日志
logging.async=true
logging.queue_size=10000

4. 批量上报

# 批量上报
buffer.buffer_size=30000
buffer.channel_size=5

日志关联

1. 配置 Logback

<!-- logback.xml -->
<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>
    
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

2. 日志输出

2026-04-10 10:00:00.000 [TID: N/A] [main] INFO  c.e.controller.UserController - 收到请求
2026-04-10 10:00:00.100 [TID: 123456.0.1] [main] INFO  c.e.service.UserService - 查询用户
2026-04-10 10:00:00.200 [TID: 123456.0.1] [main] INFO  c.e.repository.UserRepository - 执行 SQL

3. 日志查询

在 SkyWalking UI 的”日志”页面:

告警配置

1. 告警规则

# alarm-settings.yml
rules:
  # 服务响应时间告警
  service_resp_time_rule:
    metrics-name: service_resp_time
    op: ">"
    threshold: 1000
    period: 10
    count: 3
    silence-period: 5
    message: 服务 {name} 响应时间超过 1000ms
    
  # 服务成功率告警
  service_sla_rule:
    metrics-name: service_sla
    op: "<"
    threshold: 80
    period: 10
    count: 2
    silence-period: 5
    message: 服务 {name} 成功率低于 80%
    
  # 链路追踪告警
  trace_duration_rule:
    metrics-name: trace_duration
    op: ">"
    threshold: 5000
    period: 10
    count: 5
    silence-period: 5
    message: 链路 {name} 耗时超过 5000ms

2. 告警通知

# webhook 通知
webhooks:
  - http://alert-manager:9093/alert

# 钉钉通知
dingtalk:
  webhooks:
    - url: https://oapi.dingtalk.com/robot/send?access_token=xxx
      secret: xxx
      
# 企业微信通知
wechat:
  webhooks:
    - url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx

最佳实践

1. 追踪规范

2. 性能优化

3. 故障排查

4. 监控告警

常见问题

1. 链路不完整

问题:链路追踪数据不完整

解决方案

2. 性能影响

问题:接入 Agent 后性能下降

解决方案

3. 日志不关联

问题:日志没有 Trace ID

解决方案

总结

SkyWalking 链路追踪功能可以追踪请求在分布式系统中的完整调用链路,帮助快速定位性能瓶颈和故障根源。

通过合理的配置和优化,可以在不影响性能的前提下,实现全面的链路追踪和监控。

在生产环境中,建议建立完善的告警机制,并结合日志分析,提高故障排查效率。


分享这篇文章到:

上一篇文章
Spring Boot WebFlux 响应式编程
下一篇文章
Spring Boot 虚拟线程实战