单体应用时代,一个请求的处理过程在一个进程里,出问题了加日志、打断点,慢慢就能看清。微服务拆分把这个透明度彻底打碎了。一个下单请求,从网关进来,要经过订单、库存、支付、营销、消息十几个服务,每个服务又调几个下游。某个请求慢了,到底慢在哪个环节。报错了,是哪个服务出的,是它自己的 bug 还是它依赖的下游超时。没有链路追踪,这些问题只能靠猜,猜得中靠运气,猜不中就是几个团队互相甩锅。
链路追踪要做的事情,就是把微服务的黑盒重新打开。它给每个请求打上一个全局唯一的标识,让这个标识随请求穿过所有服务,最后把整条调用链还原出来,每个环节花了多少时间、是否报错,一目了然。这篇文章讲链路追踪的核心机制,以及真正在生产里把它用好要注意什么。
图:一次请求生成 TraceId,跨服务传递并汇聚到追踪平台
可观测性的三个支柱
讲链路追踪前,先把它放到更大的可观测性框架里。可观测性有三个支柱,指标 Metrics、日志 Logging、链路 Tracing,三者各有分工。
指标是聚合数据,QPS、响应时间、错误率这些。它告诉你系统的整体状态,错误率飙升了,但不知道是哪个请求、哪个环节出了问题。日志是离散事件,记录某个时刻发生了什么,比如某个用户某个操作触发了某个异常。它告诉你具体发生了什么,但要你自己去关联。链路是请求级的追踪,把一个请求经过的所有服务和操作串起来。它告诉你某个具体请求经历了什么。
三者配合形成完整的排障闭环。指标告诉你系统出问题了,错误率飙升。链路告诉你哪个环节出问题了,比如支付服务这一段慢。日志告诉你具体什么错误,比如支付服务里抛了空指针。缺了任何一个,排障链路就断了。链路追踪是这三者里专门解决分布式下定位难问题的那一环。
图:可观测性三支柱——指标发现系统出问题,链路定位到具体环节,日志查到具体错误,三者用 TraceId 串成排障闭环
Trace 和 Span,链路追踪的核心概念
链路追踪有两个核心概念,Trace 和 Span。
Trace 是一次完整的分布式请求。一个用户下单,从请求进网关到最终返回,这一整条链路就是一个 Trace,有一个全局唯一的 TraceId。
Span 是请求中的一个操作。一次服务调用、一次数据库查询、一次缓存读取,都是一个 Span。每个 Span 有自己的 SpanId 和父 SpanId。父 SpanId 把所有 Span 串成一棵调用树,根 Span 是链路的入口,它的子 Span 是它直接发起的调用,子 Span 又有自己的子 Span,层层展开。
整条链路的耗时,等于最外层 Span 的耗时,而每个子 Span 的耗时叠加起来,能清楚看出时间花在了哪。可视化后,哪个服务、哪次调用是瓶颈,一眼可见。这就是链路追踪的价值,把分布式调用的耗时和调用关系完整呈现出来。
图:Trace 与 Span 调用树——一次请求生成全局唯一 TraceId,各操作为 Span,靠 parentId 串成树,耗时叠加即可定位瓶颈
上下文透传,串联整条链路的关键
链路追踪要能工作,前提是 TraceId 能全链路透传。请求从服务 A 进服务 B 时,B 必须知道这次调用属于哪个 Trace,否则链路就断在 A 和 B 之间。
透传靠的是请求头的注入和提取。服务 A 处理请求时生成 TraceId 和自己的 SpanId,调用服务 B 时把这些信息注入到请求头里,比如 HTTP 的 X-B3-TraceId。服务 B 收到请求后从请求头里提取出 TraceId,沿用这个 TraceId,再生成自己的新 SpanId,把 A 的 SpanId 记为父 SpanId。这样一路传下去,整条链路就串起来了。
不同协议的透传机制不同。HTTP 走请求头,RPC 走框架的上下文比如 Dubbo 的 attachment,消息队列把 TraceId 放在消息属性里,消费时继承。线程池这种异步场景要特别处理,因为 ThreadLocal 不会自动跨线程传递,任务包装时要把上下文手动传过去。
图:上下文透传——服务 A 注入 TraceId/SpanId 到请求头,服务 B 提取并沿用,层层传递串起整条链路;漏传处即为黑盒
链路追踪的价值完全依赖 TraceId 全链路透传。任何一个环节漏传,比如某次自定义的异步调用、某个第三方 SDK 的内部调用没接埋点,链路就在那里断掉,排查时这段就是黑盒。成熟的框架自动处理主流场景,但自定义的异步调用和线程池任务,必须开发者自己确保透传。
自动埋点和手动埋点
Span 不是凭空产生的,要靠埋点。埋点分自动和手动两种。
自动埋点靠链路追踪 SDK 自动拦截主流框架的调用。HTTP 客户端、RPC 框架、数据库驱动、消息队列客户端,这些 SDK 都能自动识别并生成 Span。它的好处是覆盖了绝大多数技术调用,业务代码不用改。
手动埋点是开发者自己在业务关键逻辑里加 Span。比如风控校验这个环节,自动埋点只会看到一个外部调用,不知道里面做了什么。手动加一个 Span 标记风控校验的开始和结束,就能看到这个业务环节耗时多少。
两者结合,既有技术调用的全貌,也有业务环节的细节。只靠自动埋点,看到的是一堆 RPC 和 SQL,看不出业务语义。只靠手动埋点,工作量大还容易遗漏。生产里通常是自动埋点打底,手动埋点补充业务关键节点。
OpenTelemetry,统一采集标准
链路追踪早年碎片化严重,各家有自己的 SDK 和数据格式,换一个后端就得改一遍埋点代码。OpenTelemetry 是 CNCF 主推的可观测性标准,统一了指标、日志、链路的采集 API 和数据格式。
它的架构分三层。应用用 OpenTelemetry SDK 采集数据,Collector 作为中间层统一接收和处理,后端可以对接 Jaeger、Zipkin 或者各类商业 APM。这种分层最大的好处是采集和后端解耦,换后端不用改应用代码。早年没这个标准时,从一个 APM 换到另一个,埋点代码要重写,工程量很大。有了 OpenTelemetry,应用层一次接入,后端随意切换。
采样策略,平衡成本与可查性
全量链路上报的数据量是惊人的。一个请求十几二十个 Span,QPS 一高,每天产生的 Span 数是天文数字,全量存储成本极高。采样是必须的。
采样有几种策略。固定采样,按固定比例比如百分之一采样,简单均匀,但可能正好漏掉出问题的请求。异常优先采样,错误请求和慢请求必采,正常请求按比例采样,这是生产里最常用的策略,因为它保证出问题的请求一定能查到。尾部采样,请求完成后根据结果决定是否采样,能保证异常全采,但实现复杂,因为要等请求完成才能决定。
异常优先是性价比最高的选择。正常请求采样降低成本,错误和慢请求必采保证问题可查。一个常见的反模式是图省事用固定采样,结果某次故障正好采样率太低,关键请求没采到,事后想复盘却无线索。
把 TraceId 打进日志
链路追踪最大的价值不在全量记录,在排查问题时能快速定位。而这个定位要真正高效,关键一步是把 TraceId 打进业务日志。
日志系统里如果每条日志都带着 TraceId,那么从链路定位到某个服务慢之后,可以用这个 TraceId 在日志系统里搜出该请求在这个服务里的所有日志,看到具体的异常堆栈和上下文。没有这个 TraceId 的关联,链路和日志是两套割裂的系统,从链路跳到日志要靠时间戳或者业务 ID 去猜着匹配,效率低很多。
把三者串起来看完整的排障路径。指标告警触发,错误率飙升。用链路追踪定位到是支付服务慢。用 TraceId 在日志里搜出该请求的详细日志,看到具体是哪个空指针或者哪次超时。指标、链路、日志,三者用 TraceId 串成一条完整的排查闭环。这个闭环搭起来了,微服务的排障才从靠猜变成了靠看。
让分布式系统重新透明
链路追踪做的事情,是把微服务拆分打碎的透明度重新拼回来。单体时代一个进程内能看清楚的调用链,在微服务时代要靠 TraceId 全链路透传加 Span 记录来还原。
这套机制的核心是 TraceId 全链路透传加 Span 记录各环节耗时。自动埋点覆盖技术调用,手动埋点补充业务语义,OpenTelemetry 统一采集标准让后端可切换,采样策略选异常优先平衡成本与可查性,最后把 TraceId 打进日志让三者串联成闭环。
这些做扎实了,分布式系统的可观测性才算立住。没有链路追踪的微服务,排障只能靠猜,几个团队互相推诿责任。有了它,定位问题从玄学变成了工程,一个 TraceId 就能还原一条调用链的全貌。这是微服务规模化后必备的基础设施,不是可选项。








