微服务分布式链路追踪的原理是什么?Spring Cloud Sleuth 现在还能用吗?
简化版
分布式追踪用 Trace 表示一次跨服务调用链,用 Span 表示其中一个操作,并通过 HTTP 或消息头传播 Trace Context,使各服务生成的 Span 能关联起来。Spring Boot 3 时代 Sleuth 的能力已迁入 Micrometer Tracing,Sleuth 不支持 Spring Boot 3,新项目应使用 Micrometer Observation/Tracing 及对应导出器。
详细版
入口收到请求时创建或继续 Trace,当前服务创建 Span,调用下游前注入 traceId、spanId 和采样标记,下游提取上下文后创建子 Span。完成后的数据按采样策略导出到 Zipkin、OTLP 兼容后端等系统,日志可通过 MDC 关联 traceId。
链路传播依赖受支持客户端和线程上下文。手动创建线程、未被观测的 HTTP 客户端或消息消费者可能丢失上下文;响应式代码还依赖 Reactor Context,不能只用 ThreadLocal 思路处理。
完整版教学
一、Trace 与 Span
Trace: 用户下单
Span: Gateway 接收请求
Span: OrderService 创建订单
Span: InventoryService 扣库存
Span: PaymentService 发起支付
同一 Trace 共享 traceId,每个 Span 有自己的 spanId 和父子关系,并记录开始时间、耗时、状态、事件与有限属性。它能展示延迟花在哪一跳,但不能自动解释业务为什么错误。
| 概念 | 粒度 | 典型字段 | 作用 |
|---|---|---|---|
| Trace | 一次完整请求链 | traceId | 把跨服务调用串起来 |
| Span | 一次操作或一次远程调用 | spanId、parentId、耗时 | 定位每一跳耗时和状态 |
| Context | 当前追踪上下文 | traceId、spanId、sampled | 在线程、HTTP、消息之间传播 |
| Baggage | 随链路传播的少量业务上下文 | tenant、region | 给下游读取,但要严格控制 |
记忆钩子:Trace 是整条线,Span 是线上每一段,Context 是把线从一个进程接到下一个进程的接头。
二、上下文如何跨进程
调用方把追踪上下文注入请求头或消息元数据,下游提取后继续链路。常见传播格式包括 W3C Trace Context 和 B3,所有服务必须配置兼容格式;格式不一致会产生断裂的新 Trace。
网关、Feign、RestClient、WebClient 和消息组件在相应观测集成存在时可自动传播。自定义客户端要使用 Tracing API 或已观测的构建器,不能手工拼一个 traceId 就认为完成了追踪。
三、Observation、Tracing 与 Metrics
Micrometer Observation 描述一次可观测操作,Handler 可据此产生指标或追踪数据。Tracing Bridge 再对接 Brave、OpenTelemetry 等 tracer 实现,导出器负责把数据发送到后端。
抽象层、实现层和后端要分开:Micrometer Tracing 不等于某个具体追踪存储,使用 OTLP 也不代表必须绑定某一家平台。
四、采样为什么必要
全量追踪在高流量系统会产生大量 CPU、网络和存储开销,因此通常只采样部分 Trace。未采样请求仍可能传播基本上下文,但不会生成完整可导出数据,排查时不能把“后端搜不到”直接等同于请求没发生。
采样率应结合错误率、流量和成本设置。需要保留所有错误链路时,还要看所用 tracer 与后端是否支持尾部采样等能力,不能只依赖入口随机采样。
QPS = 10000
每条 Trace 平均 8 个 Span
全量:10000 × 8 = 80000 Span/s
采样 1%:约 800 Span/s
这个数字说明为什么高流量系统很少无脑全量追踪。采样降低成本,但也会让低频问题更难复现;所以常见策略是普通请求低采样,错误、慢请求或重点租户提高保留比例。
五、标签与 Baggage 风险
Span tag 用于检索和分析,但订单号、用户 ID 等高基数值会增加后端索引成本。密码、Token 和完整请求体不应写入 Span 或 Baggage。
Baggage 会随调用传播,字段越多,请求头和消息体积越大。只有下游确实需要的少量上下文才适合传播,业务数据应通过正式接口参数传递。
六、异步与日志关联
线程池切换会破坏单纯 ThreadLocal 上下文,必须使用框架提供的上下文传播支持。Reactor 可能在同一线程交错处理多个请求,更不能把追踪状态永久放在线程变量中。
日志中的 traceId 方便从错误日志跳到调用链,但日志、指标和追踪各有职责:指标发现异常趋势,追踪定位跨服务耗时,日志提供具体事件,三者应互相链接而不是互相替代。
面试里可以按排障顺序表达:
- 先看指标判断是不是整体变慢或错误率升高;
- 再用 traceId 找到具体 Trace,定位是哪一跳耗时异常;
- 最后回到日志查看该 Span 附近的业务事件和异常堆栈。
这样回答能说明链路追踪不是孤立工具,而是可观测性三件套中的一环。
七、常见误区与追问
- 误区:有 traceId 就等于完整链路追踪。 只有上下游都正确传播 Context 并创建 Span,才能还原父子关系和耗时。
- 误区:Sleuth 在 Spring Boot 3 新项目里仍是默认方案。 Sleuth 属于 Boot 2 时代,新项目应使用 Micrometer Observation/Tracing。
- 追问:W3C Trace Context 和 B3 为什么要统一? 传播格式不一致会导致上下游无法识别同一上下文,链路被切成多个 Trace。
- 追问:为什么不能把用户 ID 随便放进 tag? 高基数字段会显著增加后端索引和存储成本,敏感字段还会带来合规风险。
- 误区:追踪可以替代日志和指标。 指标看趋势,追踪看跨服务路径,日志看具体事件,三者互补。
- 追问:异步线程为什么容易丢 Trace? ThreadLocal 不会自动跨线程传播,线程池和 Reactor 需要框架提供的上下文传播机制。
八、加强记忆
Trace 串整条链,Span 记一次操作,上下文靠标准请求头或消息元数据跨进程传播。Boot 3 使用 Micrometer Observation/Tracing,Sleuth 属于 Boot 2 时代;采样、异步上下文、高基数标签和敏感 Baggage 是落地时的关键边界。