本节摘要:当一个请求跨过十几二十个微服务,散落在每台机器的日志就只能拼出碎片。分布式链路追踪用一个贯穿全链路的 traceID 和一组 span,把这些碎片还原成一条完整的调用链。本节讲 span、traceID、span context 的构成与采样,回顾 OpenTelemetry 这类开放标准的意义,并用一个"从订单到支付的五跳链路"完整展示它如何工作。
第三章我们靠 trace_id 把日志串起来,但那只解决了"同一笔请求的日志聚集"。真正难的是:这笔请求在十几个服务之间走了什么路线、每一步花了多久、哪里堵了。光靠日志要拼出这条链路得靠肉眼和时间戳,服务一多就拼不动了。链路追踪器就是专门干这个活的东西——它把一次请求的完整旅程录成一段可回放的结构化记录。
链路追踪的世界只有两个核心概念。
span:一次请求中的"一个工作单元"。比如服务 A 收到请求并调用服务 B,就是一个 span。每个 span 记录三件事:名字(做了什么)、时间(从何时到何时)、关系(谁是父 span、谁是子 span)。span 还会带一些属性和状态(成功/失败/具体错误)。
trace:一整段请求的完整轨迹。它由多个 span 通过父子关系连成一条链,像一份家谱。标识它的就是 traceID——在请求刚开始时就生成并贯穿全程。
它们的嵌套逻辑很容易理解:一个 span 发起子 span,子 span 再发起孙子 span,形成一棵 span 树,根是不变的 traceID。
走一个真实的例子:用户下单,从前端网关 → 订单服务 → 支付服务 → 账户服务,再到账。大概的 span 树长这样:
T1(根)
如果支付服务的 span3 耗了 200ms,占比整条 300ms 的 2/3,你一打开这条 trace 的火焰图立马能看出来瓶颈在服务 B 的支付调用处,而不是在页面慢。
链路追踪一点也不玄乎,它靠的是"上下文传递"。请求从服务 A 到服务 B 时,A 必须把当前 span 的 context 一起传过去,一般是塞进请求头(HTTP 头里的 traceparent 就干这个)。服务 B 收到后以此为父 span 建立子 span,再把 context 传给服务 C。如此层层下传,一只无形的手把整个调用串成了链。
如果哪个服务没把 context 传下去,那条分支就断了——所以埋点的一个核心纪律是"每个出站调用都要传 span context"。很多人第一步实现不了全链路,根因往往就是中间某个服务忘了透传。
早期各家实现互不互通(SkyWalking、Zipkin、Jaeger 各玩各的),后来业界用 OpenTelemetry(OTel)达成了统一:它定义了 span 的规范、injection/extraction 的标准、以及一套采样接口。只要各服务的探针都按 OTel 埋点,就能被不同后端统一采集、展示。
对使用者来说,记住一句话:新的链路埋点优先走 OpenTelemetry,不要被某个私有协议绑架。它对标"Metrics、Logs、Traces 三者的统一上下文",正是第一章三支柱的落地标准。
链路数据是三大支柱里最大的存储开销之一(见 1.3 的取样本钱)。所以普遍的做法是采样:
扣住这个原则,你既不会漏掉"该查的",又能把存储成本压下来。采样率和 error 全采通常在发送端或 collector 层配置,一旦配置错误,最典型的现象是"从监控看有错误,但追踪里翻不到那条 trace"——排查时要注意这个坑。
一条 span 记的属性别贪多,真正值钱的就三样,排查时看得最快:status(成功/失败,失败还要带 error code 与 message)、duration(耗时,是定位瓶颈的第一手证据)、attributes(几个关键的业务标签,比如 user_id、order_id、http.method)。为什么只需要这三个?因为链路追踪的用途不是"给每个细节都留档",而是"最快回答这笔请求成没成、卡在哪、是谁的请求"。属性堆得越碎,越容易在写入时就把采集端拖垮,也在展示时淹没了真正有用的三个字段。贴着这三个维度写 span,后续 APM(下一节)聚合出的性能地图才干净可用。
很多初用链路的人会给一个服务内部也层层叠 span,把循环里的每一句代码都包成 span,结果一棵树深到十几层,火焰图看半天找不到重点。合理粒度是"span 对齐一次真实的跨越 组件边界的调用":进一个服务一次 span、调一个重要下游一次 span、一次关键的持久化操作一次 span,够了。函数内部的循环、纯内部计算,不必都包成子 span——那是把链路当 profiler 用,既费存储又伤可读性。宁可 span 数少而含义清楚,也别堆一堆表达不了诊断价值的碎片。
单条链路我们看懂了,下一节把它放大——APM 把上千条链路画成拓扑图、算成性能指标,让你一眼看出全系统哪条调用最痛。