1. 引言
在现代的面向服务架构 (SOA)、微服务和云方案里,一个用户请求可能会在返回结果前“飞过”几十个不同的服务。每个节点都写自己的日志,但怎么知道某个位置的错误或延迟真的是和这个特定请求有关?
分布式追踪 就像物流里的包裹追踪系统:你能看到包裹经过了哪些站点、在哪儿滞留了、哪里出了问题。在编程世界里——它让你能追踪一个请求在所有服务中的“旅行”,知道每段耗时多少、哪里发生了错误或延迟。
为什么需要追踪?
- 定位错误:出问题时你能马上看到在哪个服务、哪个阶段、甚至是哪个具体方法被调用。
- 发现“瓶颈”:追踪会显示哪部分服务“慢”,在哪个阶段消耗最多时间。
- 用户场景分析:可以理解客户真实如何使用你的服务,该把优化投入到哪儿。
它怎么工作?
每个请求会拿到一个唯一的追踪标识(TraceId),这个标识会随着请求在整个服务栈中“旅行”:从前端到后端,从 API 到数据库再回来。每个阶段会创建 span(spans),记录操作的耗时和额外事件。最终你会得到一个操作树(或图),带有每个节点的持续时间和它们之间的关联。
分布式追踪的关键实体与术语
| 术语 | 描述 |
|---|---|
| 追踪(Trace) | 一个请求穿过多个服务的逻辑路径。由唯一的 TraceId 标识。 |
| span(Span) | 追踪内的一个操作或子操作。每个 span 有自己的唯一标识。 |
| TraceId | 整个追踪(整个请求)的唯一标识。 |
| SpanId | 当前 span(操作)的唯一标识。 |
| Parent SpanId | 父 span 的 Id(如果该 span 是另一个操作的一部分)。 |
| Attributes | span 的一组用户元数据:方法名、参数、状态等。 |
| Events/Logs | 与 span 相关的重要事件(比如错误、完成)。 |
| Context Propagation | 在服务和线程间传递追踪信息的机制。 |
2. OpenTelemetry: 用于可观测性的开放标准
关于 OpenTelemetry 简要说明
OpenTelemetry — 一个开放标准和工具集,用来从各种应用收集遥测(追踪、指标、日志)。它由 Cloud Native Computing Foundation (CNCF) 支持,已经成为云和分布式应用领域的事实标准。
OpenTelemetry(简称 OTel)可以:
- 自动收集并发送追踪、指标和日志;
- 支持主流编程语言,包括 C#, Java, Python 等;
- 将数据传回可观测性系统——Jaeger, Zipkin, Azure Monitor, Grafana 等;
- 通过插件扩展并按任何技术栈进行配置。
为什么选择 OpenTelemetry?
- 厂商无关:OTel 是标准,而不是某个公司的产品。
- 兼容性:支持主流追踪格式,易于和 APM 系统集成。
- 自动化:很多工作(比如 HTTP 请求追踪)可以自动完成,配置很少。
- 可扩展性:OTel 既适合小系统,也能应对非常大的系统。
OpenTelemetry 架构:简单说明
为了不绕晕我们,来可视化一个典型的 OTel 追踪数据流。
flowchart LR
subgraph 应用
A[OpenTelemetry SDK]
end
A -->|追踪、指标、日志| B[OpenTelemetry Collector]
B -->|导出到| C[Jaeger/Zipkin/Grafana/Azure/Application Insights 等]
- OpenTelemetry SDK:直接嵌入到你的应用里(例如通过 .NET 的 NuGet 包)。
- OTel Collector:一个独立的汇聚服务(通常以 Docker 容器部署),接收所有应用的 trace 数据并把它们导出到指定位置。
- 可视化前端:你查看漂亮图表、树状视图、过滤和分析追踪的地方。
3. 在 C#/.NET 上快速开始追踪
我们来给教学示例应用添加基本的分布式追踪。主要步骤很简单。
安装 NuGet 包
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.Console
OpenTelemetry — SDK 的基础实现;
OpenTelemetry.Exporter.Console — 将追踪导出到控制台(可以替换为 Jaeger、Zipkin 等)。
最小追踪配置
在文件 Program.cs(针对控制台应用):
using OpenTelemetry;
using OpenTelemetry.Trace;
class Program
{
static void Main(string[] args)
{
// 配置追踪
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource("DemoApp") // 指定源名称
.AddConsoleExporter() // 将追踪导出到控制台
.Build();
var source = new System.Diagnostics.ActivitySource("DemoApp");
// 开始一个新的追踪(根 span)
using (var activity = source.StartActivity("主操作"))
{
DoWork();
}
}
static void DoWork()
{
// 一些应用逻辑...
System.Threading.Thread.Sleep(300);
}
}
这里发生了什么?
- 创建了追踪提供者,注册了源("DemoApp")并添加了控制台导出器 AddConsoleExporter()。
- 通过 ActivitySource 在程序入口启动了一个新的 activity(span)。
- 在内部执行我们想要追踪的实际工作。
结果:
Activity.Id: 0b69e3e97ca5f14d
Activity.Operation: 主操作
Duration: 00:00:00.3001082
...
HTTP 和数据库的自动追踪
OpenTelemetry 支持 instrumentation —— 对流行库的自动追踪。比如,通过 HttpClient 的调用和针对数据库的查询(ADO.NET)可以在不改你业务代码的情况下被采集。
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddHttpClientInstrumentation() // HTTP 请求
.AddSqlClientInstrumentation() // SQL 请求(如果你在用)
.AddConsoleExporter()
.Build();
现在你通过 HttpClient 的所有调用和对 SQL 的操作会自动出现在追踪里。
4. 追踪的可视化
输出到控制台只是第一步。要真正发挥价值,最好用带可视化的前端。
Jaeger
Jaeger 是最流行的开源追踪可视化系统之一。
- 部署 Jaeger(比如用 Docker)。
- 把 AddConsoleExporter() 换成:
.AddJaegerExporter(options => { options.AgentHost = "localhost"; options.AgentPort = 6831; }) - 现在所有追踪都会显示在 Jaeger UI 中——可以按 TraceId 过滤,查看详细的时间线。
完整指南:OpenTelemetry Jaeger Exporter 用于 .NET
5. 有用的细节
方法比较
不使用 OTel 时你得手动把 TraceId 写到日志里,希望别忘了往下传,然后在分析事故时受罪。用 OTel 后整条链自动串起来,一键可视化。
| 方法 | 工作量 | 可靠性 | 可扩展性 | 兼容性 |
|---|---|---|---|---|
| 手动记录 TraceId | 高 | 低 | 需要持续维护 | 仅限你的应用 |
| OpenTelemetry | 最小化(部署后) | 可靠 | 广泛(任意语言、技术栈) | 与大多数 APM 和日志系统兼容 |
架构示例
[网页客户端] --> [API 服务] --> [授权服务] --> [数据库服务]
当用户点击按钮,请求会通过这些服务。借助 OTel 你可以把那根线(TraceId)从每个服务串起来,自动把各段逻辑合并成一条追踪。
- 在每个服务里 OTel SDK 会自动从 HTTP header 识别 TraceId 并继续链路。
- 结果是你可以打开一个追踪——看哪些地方耗了毫秒,发生了什么,哪里出错了。
在真实项目和面试中的应用
- 微服务系统和分布式应用(金融科技、市场平台、SaaS 等)。
- DevOps/SRE:快速定位问题。
- 提升 SLA 和服务响应能力。
- 性能分析与优化。
- 面试中展示成熟的工程实践。
6. 常见错误和实现注意点
忘了传递 TraceContext。 如果服务间不传 TraceId(比如忘记把必要的 HTTP header 透传),追踪会被断开——看起来像孤立的“点”,可观测性的好处就没了。
未被 instrument 的代码。 如果某个库或框架不支持自动 instrument,那些操作会从追踪里缺失(不过你可以通过 ActivitySource 和 StartActivity() 手动创建 span)。
数据过多。 为每个请求采集追踪在大流量下代价很高。通常只对部分请求开启追踪(sample rate),或基于触发条件采集(错误、慢请求等)。
性能影响。 正确使用异步导出时 OTel 对性能的影响很小,但在极大流量下仍需关注开销并调整采样率。
GO TO FULL VERSION