链路追踪原理
链路追踪的核心是把一次跨服务的请求,通过统一的 Trace / Span / Segment 模型还原成完整调用链。理解这些概念的生成、传播与合并过程,才能看懂 Trace 页面上的每一行数据,也才能在遇到断链时知道去哪排查。
Trace / Span / Segment
SkyWalking 在 Dapper 模型基础上引入 **Segment(段)**这一层概念,用于组织单个进程内的数据上报。
| 概念 | 含义 |
|---|---|
| Trace | 一次分布式请求的完整调用路径,由 traceId 唯一标识 |
| Span | Trace 中的一段操作,例如一次 HTTP Client Call 或 SQL Query |
| Segment | 某个进程内产生并上报的一组 Span |
Segment 与 Trace/Span 的关系:一次调用进入一个进程后,该进程内产生的所有 Span 被打包成一个 Segment 上报给 OAP;OAP 再把这些跨进程的 Segment 按 traceId 合并成完整 Trace。
text
Trace (traceId = T1)
├── Segment A(进程 A,含 Span A1, A2)
│ └── 发起对 B 的调用(携带 T1)
├── Segment B(进程 B,含 Span B1)
│ └── 发起对 DB 的调用(携带 T1)
└── Segment C(进程 C,含 Span C1)Span 的关键字段
一个 Span 至少包含:
| 字段 | 作用 |
|---|---|
| traceId | 所属链路标识 |
| spanId / parentSpanId | 本 Span 及父 Span 的 ID,用于建树 |
| operationName(Segment 名) | 如 HTTP /api/order、SQL select |
| startTime / endTime / duration | 起止与耗时 |
| component(组件) | 例如 HTTP、JDBC、Redis |
| peer(对端地址) | 下游地址,如 db:3306 |
| tags / logs | 附加信息,如 URL、SQL、错误堆栈 |
OAP 据此把 Span 按父子关系还原成树,并统计每个 Span 的耗时。
进程内上下文传播
同一进程/线程内,Span 通过 ThreadLocal(ContextManager) 传递当前追踪上下文,不落网络:
text
进入进程(Segment 开始)
└► ThreadLocal 保存当前 Span 上下文
└► 方法 A(Span A)─► 方法 B(Span B) 子 Span 链式嵌套
└► 方法结束,从 ThreadLocal 弹出
进程结束,打包成 Segment 上报关键点:
- 必须成对入栈出栈:Span 打开后要正确关闭,否则 ThreadLocal 泄漏导致链路错乱。
- 异步不自动传递:线程池、异步回调等场景 ThreadLocal 不会自动带到子线程,需显式传递上下文,否则子线程的 Span 会成为"孤儿 Span"、链路断开。
跨进程上下文传播
跨进程时,发起方把 traceId 等信息写入协议头,接收方读取后续接 Span。
text
服务 A 服务 B
│ 生成/续接 traceId │
│ 写 traceparent/sw8 到 Header │
├─────────────────────────►│ 读取 Header,新建 Segment/Span
│ │ 处理,继续向下游传播传播载体与协议:
| 载体 | 协议/Header |
|---|---|
| HTTP | sw8(SkyWalking 私有)、W3C traceparent |
| gRPC | gRPC Metadata(sw8 / traceparent) |
| MQ | 消息 Header(Kafka Header、RocketMQ 消息属性等) |
| 进程内 | ThreadLocal(ContextManager) |
SkyWalking 同时支持自有协议与 W3C Trace Context,跨厂商互通时优先用 W3C。
协议与标准的背景参见 APM / 上下文传播。
OAP:Segment 合并(Trace Merging)
OAP 收到大量 Segment 后,按以下方式合并出完整 Trace:
- 按 traceId 分桶:把一个 traceId 的所有 Segment 归到一起。
- 按 parentSpanId / spanId 建树:还原调用层级。
- 计算统计:汇总每个 Span 的耗时、每个 Service/Endpoint 的 RT。
- 生成 Topology:根据跨 Service 的调用边,统计服务间调用关系与频率。
一条完整 Trace 的展示依赖所有相关 Segment 都能"对上":只要某个中间服务的 Agent 没上报(例如 Agent 版本不兼容、被采样、断网),该段就会出现缺失或断链。
断链原因排查
在 Trace 页面看到链路"M 形断裂",按此排查:
| 可能原因 | 检查点 |
|---|---|
| 采样导致某些段缺失 | 是否开了采样,两端采样策略是否一致 |
| Header 未传播 | 下游服务是否也接入了 Agent;HTTP/MQ 头名是否被框架改写 |
| 异步丢上下文 | 线程池/异步回调是否有异步插件或手动传递 |
| Agent 未接入/上报失败 | 各服务是否都加载了 Agent、OAP 地址是否可达 |
| 中间件不透传 header | 网关、MQ 是否吞掉或重建了头 |
核心原则:全链路每个参与方都必须是"同一条 traceId、同一个上下文",缺少任何一环都会断链。