Skywalking 分布式链路追踪
大约 10 分钟约 2908 字
Skywalking 分布式链路追踪
简介
Apache SkyWalking 是一款开源的 APM(Application Performance Monitoring)系统,专为微服务、云原生和容器化架构设计。它提供了分布式链路追踪、服务性能监控、告警等功能。.NET 应用通过 SkyAPM Agent 接入,零代码侵入。
特点
安装部署
Docker 部署 SkyWalking
# 启动 Elasticsearch(存储)
docker run -d --name elasticsearch \
-p 9200:9200 \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
elasticsearch:8.12.0
# 启动 SkyWalking OAP Server
docker run -d --name oap \
-p 11800:11800 -p 12800:12800 \
-e SW_STORAGE=elasticsearch \
-e SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200 \
--link elasticsearch \
apache/skywalking-oap-server:9.7.0
# 启动 SkyWalking UI
docker run -d --name ui \
-p 8080:8080 \
-e SW_OAP_ADDRESS=http://oap:12800 \
--link oap \
apache/skywalking-ui:9.7.0.NET 项目接入
# 安装 SkyAPM Agent
dotnet add package SkyAPM.Agent.AspNetCore// skyapm.json — Agent 配置文件
{
"SkyWalking": {
"ServiceName": "my-api-service",
"Namespace": "SunnyFan",
"HeaderVersions": [ "sw8" ],
"Sampling": {
"SamplePer3Secs": -1,
"Percentage": 100.0
},
"Logging": {
"Level": "Information",
"FilePath": "logs/skyapm-{Date}.log"
},
"Transport": {
"gRPC": {
"Servers": "localhost:11800",
"Timeout": 30000,
"ConnectTimeout": 10000,
"ReportTimeout": 300000
}
}
}
}// Program.cs — 添加 SkyAPM
builder.Services.AddSkyAPM(ext => ext.AddAspNetCoreHosting());Docker Compose 一键部署
# docker-compose.yml
version: '3.8'
services:
elasticsearch:
image: elasticsearch:8.12.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
ports:
- "9200:9200"
oap:
image: apache/skywalking-oap-server:9.7.0
environment:
- SW_STORAGE=elasticsearch
- SW_STORAGE_ES_CLUSTER_NODES=elasticsearch:9200
ports:
- "11800:11800"
- "12800:12800"
depends_on:
- elasticsearch
ui:
image: apache/skywalking-ui:9.7.0
environment:
- SW_OAP_ADDRESS=http://oap:12800
ports:
- "8080:8080"
depends_on:
- oap自定义 Span 追踪
/// <summary>
/// 手动创建追踪 Span — 记录关键业务操作
/// </summary>
using SkyApm.Diagnostics;
using SkyApm.Tracing;
using SkyApm.Tracing.Segments;
public class OrderService
{
private readonly ITracingContext _tracingContext;
private readonly ILogger<OrderService> _logger;
public OrderService(ITracingContext tracingContext, ILogger<OrderService> logger)
{
_tracingContext = tracingContext;
_logger = logger;
}
public async Task<Order> CreateOrderAsync(CreateOrderCommand command)
{
// 创建自定义 Span
var span = _tracingContext.CreateExitSpan("CreateOrder");
try
{
// 添加标签 — 在 SkyWalking UI 中可以看到
span.AddTag("user.id", command.UserId.ToString());
span.AddTag("item.count", command.Items.Count.ToString());
var order = new Order { /* ... */ };
// 记录关键事件
span.AddTag("order.id", order.Id.ToString());
span.AddTag("order.amount", order.TotalAmount.ToString());
_logger.LogInformation("订单创建成功:{OrderId}", order.Id);
return order;
}
catch (Exception ex)
{
span.AddTag("error", ex.Message);
span.ErrorOccurred(ex);
throw;
}
finally
{
_tracingContext.ReleaseSpan(span);
}
}
}数据库追踪
/// <summary>
/// SkyAPM 自动追踪 EF Core 数据库调用
/// </summary>
// 安装 EF Core 追踪包
// dotnet add package SkyAPM.Diagnostics.EntityFrameworkCore
// Program.cs — 自动注册
builder.Services.AddSkyAPM(ext =>
{
ext.AddAspNetCoreHosting();
ext.AddEntityFrameworkCore(c =>
{
c.WithDbProvider("SqlServer");
c.WithPurgeRecordLimit(2000);
});
});
// EF Core 的每次查询都会自动生成一个 Span
// 在 SkyWalking UI 中可以看到 SQL 语句和执行时间HTTP 调用追踪
/// <summary>
/// SkyAPM 自动追踪 HttpClient 调用
/// </summary>
// dotnet add package SkyAPM.Diagnostics.HttpClient
builder.Services.AddSkyAPM(ext =>
{
ext.AddAspNetCoreHosting();
ext.AddHttpClient();
});
// 所有的 HttpClient 调用都会自动生成追踪 Span
public class PaymentService
{
private readonly HttpClient _httpClient;
public PaymentService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<PaymentResult> ProcessPaymentAsync(Guid orderId, decimal amount)
{
// 这个 HTTP 调用会被自动追踪
var response = await _httpClient.PostAsJsonAsync(
"http://payment-service/api/payment",
new { OrderId = orderId, Amount = amount }
);
return await response.Content.ReadFromJsonAsync<PaymentResult>();
}
}Dashboard 功能
访问 SkyWalking UI(默认 http://localhost:8080),主要功能:
| 功能模块 | 说明 |
|---|---|
| Dashboard | 服务概览 — 请求量/响应时间/错误率 |
| Topology | 服务拓扑图 — 可视化调用链路 |
| Trace | 链路追踪 — 查看单次请求的完整调用链 |
| Service | 服务指标 — P99延迟/吞吐量/SLA |
| Instance | 实例指标 — CPU/内存/CLR指标 |
| Endpoint | 接口指标 — 单个API的性能数据 |
| Alarm | 告警规则 — 配置告警通知 |
告警配置
# alarm-settings.yml — SkyWalking 告警规则
rules:
# 服务响应时间超过1秒
service_resp_time_rule:
metrics-name: service_resp_time
op: ">"
threshold: 1000
period: 10
count: 3
message: "服务 {name} 响应时间超过1秒"
# 服务错误率超过1%
service_error_rate_rule:
metrics-name: service_percent
op: "<"
threshold: 99
period: 10
count: 3
message: "服务 {name} 错误率超过1%"
# 接口P99延迟超过500ms
endpoint_p99_rule:
metrics-name: endpoint_p99
op: ">"
threshold: 500
period: 10
count: 3
message: "接口 {name} P99延迟超过500ms"
webhooks:
- http://localhost:5000/api/alert/callback自定义追踪标签
手动添加 Span 标签
// 在代码中手动添加追踪标签和日志
using SkyApm.Tracing;
using SkyApm.Common;
public class OrderService
{
private readonly ITracingContext _tracingContext;
private readonly ILogger<OrderService> _logger;
public OrderService(ITracingContext tracingContext, ILogger<OrderService> logger)
{
_tracingContext = tracingContext;
_logger = logger;
}
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
var context = _tracingContext.CreateEntrySegment(
"CreateOrder", new SampleableCarrierContext());
try
{
context.Span?.AddTag("order.userId", request.UserId);
context.Span?.AddTag("order.productId", request.ProductId);
context.Span?.AddTag("order.amount", request.Amount);
context.Span?.AddLog(DateTimeOffset.UtcNow,
"开始创建订单");
// 业务逻辑
var order = await ProcessOrderAsync(request);
context.Span?.AddLog(DateTimeOffset.UtcNow,
$"订单创建成功: {order.Id}");
context.Span?.AddTag("order.status", "success");
return order;
}
catch (Exception ex)
{
context.Span?.AddLog(DateTimeOffset.UtcNow,
$"订单创建失败: {ex.Message}");
context.Span?.ErrorOccurred(ex);
context.Span?.AddTag("order.status", "failed");
throw;
}
finally
{
context.Dispose();
}
}
private Task<Order> ProcessOrderAsync(CreateOrderRequest request)
{
return Task.FromResult(new Order
{
Id = Guid.NewGuid(),
UserId = request.UserId,
Amount = request.Amount
});
}
}
public record CreateOrderRequest(long UserId, long ProductId, decimal Amount);
public record Order(Guid Id, long UserId, decimal Amount);自定义采样策略
// 自定义采样规则(控制哪些请求被追踪)
// skywalking-dotnet 支持通过配置控制采样率
// 在 skywalking.json 中配置:
/*
{
"sampling": {
"sample_per_3_secs": -1, // 每秒采样数量(-1 = 全采样)
"percentage": 50 // 采样百分比
}
}
*/
// 生产环境建议:
// 低流量服务:全采样(sample_per_3_secs: -1)
// 高流量服务:按百分比采样(percentage: 10~30)
// 调试阶段:全采样 + 增加日志级别多服务追踪配置
跨服务追踪传播
// SkyWalking 自动在 HTTP 头中传播追踪上下文
// 传播头:
// - sw8:追踪上下文(TraceId, SegmentId, SpanId 等)
//
// 在 HttpClient 中自动传播(SkyAPM Agent 自动处理)
// 无需手动添加追踪头
// 如果使用 HttpClientFactory + 自定义 DelegatingHandler
// 确保在 Agent 环境下 Handler 不会干扰追踪头的传播
public class CorrelationHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// 不要覆盖 sw8 头(Agent 会自动添加)
// 可以添加自定义关联头
request.Headers.Add("X-Request-Id", Guid.NewGuid().ToString("N")[..8]);
return await base.SendAsync(request, cancellationToken);
}
}服务拓扑与依赖分析
// SkyWalking 自动生成服务拓扑图
// 拓扑图展示:
// 1. 服务之间的调用关系
// 2. 调用次数和错误率
// 3. 响应时间分布
// 4. 实例级别的健康状态
//
// 查看拓扑图的 URL:
// http://skywalking-ui:8080/graph
//
// 拓扑图中的关键指标:
// - 节点颜色:绿色(健康)、黄色(警告)、红色(异常)
// - 连线粗细:调用频率
// - 连线标签:平均响应时间性能优化与故障排查
Agent 性能影响
// SkyWalking Agent 对应用性能的影响
// 1. CPU 开销:约 2-5%
// 2. 内存开销:约 50-100MB
// 3. 网络开销:每秒发送追踪数据到 OAP
//
// 优化建议:
// 1. 生产环境降低采样率
// 2. 调整 Agent 上传间隔
// 3. 排除不需要追踪的命名空间
// skywalking.json 优化配置:
/*
{
"agent": {
"service_name": "my-service",
"collector_backend_service": "skywalking-oap:11800",
"ignore_suffix": ".jpg,.jpeg,.js,.css,.png,.bmp,.gif,.ico,.mp3,.mp4,.html,.svg"
},
"logging": {
"level": "Warning"
},
"sampling": {
"sample_per_3_secs": 9,
"percentage": 20
},
"transport": {
"gRPC": {
"max_message_size": 52428800,
"collect_period": 3
}
}
}
*/常见问题排查
// 1. Agent 无法连接 OAP
// 检查:skywalking-oap:11800 是否可达
// 检查:防火墙是否开放 gRPC 端口
// 日志:查看 skywalking-api-{pid}.log
// 2. 追踪数据不完整
// 原因:跨进程调用未传播 sw8 头
// 解决:确保所有服务都安装了 Agent
// 3. 数据库调用未追踪
// 原因:使用非标准数据库驱动
// 解决:检查 Agent 是否支持该驱动
// 4. 内存占用过高
// 原因:采样率太高或追踪数据积压
// 解决:降低采样率,增加上报频率
// 5. 追踪链路断裂
// 原因:异步操作(Task.Run、ThreadPool.QueueUserWorkItem)
// 解决:在异步代码中手动传播追踪上下文与日志系统集成
// 将 SkyWalking TraceId 注入到日志中
builder.Logging.AddFilter("Microsoft", LogLevel.Warning);
builder.Logging.AddFilter("System", LogLevel.Warning);
// 使用 Serilog 与 SkyWalking 集成
// 安装:SkyApm.Logging.Serilog
builder.Host.UseSerilog((context, services, loggerConfiguration) =>
{
loggerConfiguration
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", context.HostingEnvironment.ApplicationName)
.WriteTo.Console(
outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] [{TraceId}] {Message:lj}{NewLine}{Exception}");
});
// 自定义 Serilog Enricher 获取 TraceId
public class SkyWalkingTraceIdEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var traceId = SkyApm.Context.TraceContext.Current?.TraceId?.ToString();
if (!string.IsNullOrEmpty(traceId))
{
var property = propertyFactory.CreateProperty("TraceId", traceId);
logEvent.AddPropertyIfAbsent(property);
}
}
}Kubernetes 部署
Helm 部署 SkyWalking
# values.yaml 关键配置
# skywalking:
# oap:
# image:
# repository: apache/skywalking-oap-server
# tag: 9.7.0
# storage:
# elasticsearch:
# replicas: 3
# storageClass: standard
# resources:
# requests:
# memory: "2Gi"
# cpu: "1"
# limits:
# memory: "4Gi"
# cpu: "2"
#
# ui:
# image:
# repository: apache/skywalking-ui
# tag: 9.7.0
# ingress:
# enabled: true
# hosts:
# - skywalking.example.com
#
# agent:
# image:
# repository: apache/skywalking-java-agent
# enabled: true
# .NET 服务 Pod 配置(通过 InitContainer 注入 Agent)
# apiVersion: apps/v1
# kind: Deployment
# spec:
# template:
# spec:
# initContainers:
# - name: skywalking-agent
# image: apache/skywalking-dotnet-agent:latest
# command: ['sh', '-c', 'cp -r /skywalking-agent /agent']
# volumeMounts:
# - name: agent-volume
# mountPath: /agent
# containers:
# - name: app
# env:
# - name: ASPNETCORE_HOSTINGSTARTUPASSEMBLIES
# value: SkyApm.Agent.AspNetCore
# - name: SKYWALKING__SERVICE_NAME
# value: my-service
# - name: SKYWALKING__COLLECTOR_BACKEND_SERVICE
# value: skywalking-oap:11800
# volumeMounts:
# - name: agent-volume
# mountPath: /skywalking-agent
# volumes:
# - name: agent-volume
# emptyDir: {}优点
缺点
总结
SkyWalking 是 .NET 微服务链路追踪的最佳选择之一。通过 SkyAPM Agent 零侵入接入,自动追踪 HTTP/数据库/缓存调用,配合告警和拓扑图,能快速定位分布式系统中的性能瓶颈。
关键知识点
- 先分清这个主题位于请求链路、后台任务链路还是基础设施链路。
- 服务端主题通常不只关心功能正确,还关心稳定性、性能和可观测性。
- 任何框架能力都要结合配置、生命周期、异常传播和外部依赖一起看。
项目落地视角
- 画清请求进入、业务执行、外部调用、日志记录和错误返回的完整路径。
- 为关键链路补齐超时、重试、熔断、追踪和结构化日志。
- 把配置与敏感信息分离,并明确不同环境的差异来源。
常见误区
- 只会堆中间件或组件,不知道它们在链路中的执行顺序。
- 忽略生命周期和线程池、连接池等运行时资源约束。
- 没有监控和测试就对性能或可靠性下结论。
进阶路线
- 继续向运行时行为、可观测性、发布治理和微服务协同深入。
- 把主题和数据库、缓存、消息队列、认证授权联动起来理解。
- 沉淀团队级模板,包括统一异常处理、配置约定和基础设施封装。
适用场景
- 当你准备把《Skywalking 分布式链路追踪》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合 API 服务、后台任务、实时通信、认证授权和微服务协作场景。
- 当需求开始涉及稳定性、性能、可观测性和发布流程时,这类主题会成为基础设施能力。
落地建议
- 先定义请求链路与失败路径,再决定中间件、过滤器、服务边界和依赖方式。
- 为关键链路补日志、指标、追踪、超时与重试策略。
- 环境配置与敏感信息分离,避免把生产参数写死在代码或镜像里。
排错清单
- 先确认问题发生在路由、模型绑定、中间件、业务层还是基础设施层。
- 检查 DI 生命周期、配置来源、序列化规则和认证上下文。
- 查看线程池、连接池、缓存命中率和外部依赖超时。
复盘问题
- 如果把《Skywalking 分布式链路追踪》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《Skywalking 分布式链路追踪》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《Skywalking 分布式链路追踪》最大的收益和代价分别是什么?
