链路追踪
链路追踪
链路追踪简介
什么是链路追踪
链路追踪系统广义的概念是:由数据采集、数据处理和数据展示三个相对独立的模块所构成的分布式追踪系统;链路追踪系统狭义的概念是:特指链路追踪的数据采集。譬如 Spring Cloud Sleuth 就属于狭义的链路追踪系统,通常会搭配 Zipkin 作为数据展示,搭配 Elasticsearch 作为数据存储来组合使用;而 Zipkin、Pinpoint、SkyWalking、CAT 都属于广义的链路追踪系统。
个人理解,链路追踪的本质就是,通过全局唯一的 ID,将分布在各个服务节点上的同一次请求产生的数据串联起来,从而梳理出调用关系,进而辅助分析系统问题、分析调用数据并统计各种系统指标。
为什么需要链路追踪
链路追踪主要有以下作用
- 分析系统瓶颈:通过记录调用经过的每一条链路上的耗时,我们能快速定位整个系统的瓶颈点在哪里。比如你访问微博首页发现很慢,肯定是由于某种原因造成的,有可能是运营商网络延迟,有可能是网关系统异常,有可能是某个服务异常,还有可能是缓存或者数据库异常。通过链路追踪,可以从全局视角上去观察,找出整个系统的瓶颈点所在,然后做出针对性的优化。
- 分析链路调用:通过链路追踪可以分析调用所经过的路径,然后评估是否合理。比如一个服务调用下游依赖了多个服务,通过调用链分析,可以评估是否每个依赖都是必要的,是否可以通过业务优化来减少服务依赖。还有就是,一般业务都会在多个数据中心都部署服务,以实现异地容灾,这个时候经常会出现一种状况就是服务 A 调用了另外一个数据中心的服务 B,而没有调用同处于一个数据中心的服务 B。根据我的经验,跨数据中心的调用视距离远近都会有一定的网络延迟,像北京和广州这种几千公里距离的网络延迟可能达到 30ms 以上,这对于有些业务几乎是不可接受的。通过对调用链路进行分析,可以找出跨数据中心的服务调用,从而进行优化,尽量规避这种情况出现。
- 生成网络拓扑:通过链路追踪中记录的链路信息,可以生成一张系统的网络调用拓扑图,它可以反映系统都依赖了哪些服务,以及服务之间的调用关系是什么样的,可以一目了然。除此之外,在网络拓扑图上还可以把服务调用的详细信息也标出来,也能起到服务监控的作用。
- 透明传输数据:除了链路追踪,业务上经常有一种需求,期望能把一些用户数据,从调用的开始一直往下传递,以便系统中的各个服务都能获取到这个信息。比如业务想做一些 A/B 测试,这时候就想通过链路追踪,把 A/B 测试的开关逻辑一直往下传递,经过的每一层服务都能获取到这个开关值,就能够统一进行 A/B 测试。
链路追踪特性
链路追踪系统作为分布式系统可观测性的关键一环,具备以下核心特性:
| 特性 | 说明 |
|---|---|
| 低侵入性 | 通过 Java Agent、字节码增强或 SDK 方式接入,对业务代码侵入极小甚至零侵入 |
| 低性能损耗 | 采集过程异步化,单次 Span 记录通常在微秒级,对业务接口耗时影响 < 5% |
| 全链路串联 | 通过全局唯一的 TraceId 将分散在多个服务节点上的同一次请求串联起来 |
| 上下文传播 | 支持跨进程、跨线程的上下文(TraceId、SpanId、Baggage)自动传播 |
| 多维数据展示 | 提供调用链路图、调用拓扑图、依赖关系图等多种可视化视图 |
| 采样能力 | 支持按比例采样、按业务规则采样,在海量请求下平衡开销与覆盖度 |
| 协议标准化 | 遵循 OpenTelemetry、OpenTracing 等标准协议,便于跨系统对接 |
链路追踪原理
Google 发布的一篇的论文 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure,里面详细讲解了链路追踪的实现原理。Dapper 论文几乎成了现代链路追踪的理论基石,很多主流的链路追踪系统都是基于 Dapper 衍生出来的,比较有名的有 Twitter 的Zipkin、阿里的鹰眼、美团的MTrace等。
链路追踪核心概念
Dapper 提出了一些很重要的核心概念:Trace、Span、Annonation 等,这是理解链路追踪原理的前提。

Trace 和 Spans(图片来源于Dapper 论文)
- Trace (追踪) - 代表一次完整的请求。一次完整的请求是指,从客户端发起请求,记录请求流转的每一个服务,直到客户端收到响应为止。整个过程中,当请求分发到第一层级的服务时,就会生成一个全局唯一的 Trace ID,并且会随着请求分发到每一层级。因此,通过 Trace ID 就可以把一次用户请求在系统中调用的链路串联起来。
- Span (跨度) - 代表一次调用,也是链路追踪的基本单元。由于每次 Trace 都可能会调用数量不定、坐标不定的多个服务,为了能够记录具体调用了哪些服务,以及调用的顺序、开始时点、执行时长等信息,每次开始调用服务前都要先埋入一个调用记录,这个记录称为一个 Span。
- Span 的数据结构应该足够简单,以便于能放在日志或者网络协议的报文头里;也应该足够完备,起码应含有时间戳、起止时间、Trace 的 ID、当前 Span 的 ID、父 Span 的 ID 等能够满足追踪需要的信息。
- Trace 实际上都是由若干个有顺序、有层级关系的 Span 所组成一颗 Trace Tree (追踪树)。
- Annotation:用于业务自定义埋点数据,例如:一次请求的用户 ID,某一个支付订单的订单 ID 等。
数据埋点阶段
数据采集的作用就是在系统的各个不同模块中进行埋点,采集数据并上报给数据处理层进行处理。而一次请求可以分为四个阶段:
- CS(Client Send)阶段 - 客户端发起请求时埋点,需要传递一些参数,比如服务的方法名等。
- SR(Server Recieve)阶段 - 服务端接收请求时埋点,需要回填一些参数,比如 traceId,spanId。
- SS(Server Send)阶段 - 服务端返回请求时埋点,这时会将上下文数据传递到异步上传队列中。
- CR(Client Recieve)阶段 - 客户端接收返回结果时埋点,这时会将上下文数据传递到异步上传队列中。
下图显示了 Span 和 Trace 在系统中的样子。

(图片来源于 spring-cloud-sleuth 文档)
图片说明:
每种颜色表示一个跨度(有七个跨度 - 从 A 到 G)。
Trace Id = X Span Id = D Client Sent类似上面的注释,表示当前跨度的跟踪 ID 设置为 X,跨度 ID 设置为 D。此外,从 RPC 的角度来看,发生了客户端发送事件。
下图显示了 span 的父子关系:

(图片来源于 spring-cloud-sleuth 文档)
链路追踪实现
一个完整的数据链路系统大致可以分为三个相对独立的模块:
- 数据采集 - 负责数据埋点并上报。
- 数据处理 - 负责数据的存储与计算。
- 数据展示 - 负责数据的可视化展示。
数据采集
数据采集负责数据埋点并上报。数据采集有三种主流的实现方式,分别是基于日志的追踪(Log-Based Tracing),基于服务的追踪(Service-Based Tracing)和基于边车代理的追踪(Sidecar-Based Tracing)。
基于日志的追踪
基于日志的追踪的思路是:将 Trace、Span 等信息直接输出到应用日志中,然后随着所有节点的日志采集汇聚到一起,再从全局日志信息中反推出完整的调用链拓扑关系。
基于日志的追踪有以下特点:
- 侵入性小、性能影响低 - 对网络消息完全没有侵入性,对应用程序只有很少量的侵入性,对性能影响也非常低。
- 依赖于日志采集过程,导致不够实时、精准 - 直接依赖于日志采集过程,日志本身不追求绝对的连续与一致,这也使得基于日志的追踪往往不如其他两种追踪实现来的精准。另外,业务服务的调用与日志的归集并不是同时完成的,也通常不由同一个进程完成,有可能发生业务调用已经顺利结束了,但由于日志归集不及时或者精度丢失,导致日志出现延迟或缺失记录,进而产生追踪失真。
日志追踪的代表产品是 Spring Cloud Sleuth,下面是一段由 Sleuth 在调用时自动生成的日志记录,可以从中观察到 TraceID、SpanID、父 SpanID 等追踪信息。
# 以下为调用端的日志输出:
Created new Feign span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]
2019-06-30 09:43:24.022 [http-nio-9010-exec-8] DEBUG o.s.c.s.i.web.client.feign.TraceFeignClient - The modified request equals GET http://localhost:9001/product/findAll HTTP/1.1
X-B3-ParentSpanId: cbe97e67ce162943
X-B3-Sampled: 0
X-B3-TraceId: cbe97e67ce162943
X-Span-Name: http:/product/findAll
X-B3-SpanId: bb1798f7a7c9c142
# 以下为服务端的日志输出:
[findAll] to a span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]
Adding a class tag with value [ProductController] to a span [Trace: cbe97e67ce162943, Span: bb1798f7a7c9c142, Parent: cbe97e67ce162943, exportable:false]基于服务的追踪
基于服务的追踪是目前最为常见的实现方式,被 Zipkin、Pinpoint、SkyWalking 等主流链路追踪系统广泛采用。其实现思路是:通过某些手段给目标应用注入追踪探针(Probe),针对 Java 应用一般就是通过 Java Agent 注入的。探针在结构上可视为一个寄生在目标服务身上的小型微服务系统,它一般会有自己专用的服务注册、心跳检测等功能,有专门的数据收集协议,把从目标系统中监控得到的服务调用信息,通过另一次独立的 HTTP 或者 RPC 请求发送给追踪系统。
基于服务的追踪有以下特点:
- 侵入性强,会有性能损耗
- 追踪更加精准、稳定
因此,基于服务的追踪会比基于日志的追踪消耗更多的资源,也有更强的侵入性,换来的收益是追踪的精确性与稳定性都有所保证,不必再依靠日志归集来传输追踪数据。
基于边车代理的追踪
基于边车代理的追踪是服务网格的专属方案,也是最理想的分布式追踪模型,它对应用完全透明,无论是日志还是服务本身都不会有任何变化;它与编程语言无关,无论应用采用什么编程语言实现,只要它还是通过网络(HTTP 或者 gRPC)来访问服务就可以被追踪到;它有自己独立的数据通道,追踪数据通过控制平面进行上报,避免了追踪对程序通信或者日志归集的依赖和干扰,保证了最佳的精确性。如果要说这种追踪实现方式还有什么缺点的话,那就是服务网格现在还不够普及,未来随着云原生的发展,相信它会成为追踪系统的主流实现方式之一。还有就是边车代理本身的对应用透明的工作原理决定了它只能实现服务调用层面的追踪,本地方法调用级别的追踪诊断是做不到的。
现在市场占有率最高的代理 Envoy 就提供了相对完善的追踪功能,但没有提供自己的界面端和存储端,所以 Envoy 和 Sleuth 一样都属于狭义的追踪系统,需要配合专门的 UI 与存储来使用,现在 Zipkin、SkyWalking 、Jaeger、LightStep Tracing 等系统都可以接受来自于 Envoy 的追踪数据,充当它的界面端。
数据处理
数据处理负责数据的存储与计算,就是将数据采集的数据按需计算,然后落地存储供查询使用。
数据处理的需求一般分为两类,一类是实时计算需求,一类是离线计算需求。
实时计算需求对计算效率要求比较高,一般要求对收集的链路数据能够在秒级别完成聚合计算,以供实时查询。而离线计算需求对计算效率要求就没那么高了,一般能在小时级别完成链路数据的聚合计算即可,一般用作数据汇总统计。针对这两类不同的数据处理需求,采用的计算方法和存储也不相同。
- 实时数据处理:针对实时数据处理,一般采用 Flink、Storm、Spark Streaming 来对链路数据进行实时聚合加工,存储一般使用 OLTP 数据仓库,比如 HBase,使用 traceId 作为 RowKey,能天然地把一整条调用链聚合在一起,提高查询效率。
- 离线数据处理:针对离线数据处理,一般通过运行 MapReduce 或者 Spark 批处理程序来对链路数据进行离线计算,存储一般使用 Hive。
数据展示
数据展示层的作用就是将处理后的链路信息以图形化的方式展示给用户。
实际项目中主要用到两种图形展示,一种是调用链路图,一种是调用拓扑图。
调用链路图
调用链路图一般展示服务总耗时、服务调用的网络深度、每一层经过的系统,以及多少次调用。调用链路图在实际项目中,主要是被用来做故障定位,比如某一次用户调用失败了,可以通过调用链路图查询这次用户调用经过了哪些环节,到底是哪一层的调用失败所导致。
下面是 Zipkin 的调用链路图:

调用拓扑图
调用拓扑图一般展示系统内都包含哪些应用,它们之间是什么关系,以及依赖调用的 QPS、平均耗时情况。调用拓扑图是一种全局视野图,在实际项目中,主要用作全局监控,用于发现系统中异常的点,从而快速做出决策。比如,某一个服务突然出现异常,那么在调用链路拓扑图中可以看出对这个服务的调用耗时都变高了,可以用红色的图样标出来,用作监控报警。
下面是 Pinpoint 的调用链路图:

链路追踪主流技术
链路追踪的主流开源产品比较丰富,主要有:
- Zipkin - Zipkin 是 Twitter 开源的调用链分析工具,目前基于 spring-cloud-sleuth 得到了广泛的使用,特点是轻量,使用、部署简单。
- Pinpoint - 是韩国人开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI 功能强大,接入端无代码侵入。
- SkyWalking - 是本土开源的基于字节码注入的调用链分析,以及应用监控分析工具。特点是支持多种插件,UI 功能较强,接入端无代码侵入。目前已加入 Apache 孵化器。
- CAT - CAT 是美团点评开源的基于编码和配置的调用链分析,应用监控分析,日志采集,监控报警等一系列的监控平台工具。
- OpenTelemetry - OpenCensus 和 OpenTracing 两个项目的合并。OpenTelemetry 是工具、API 和 SDK 的集合。用于检测、生成、收集和导出遥测数据(指标、日志和和追踪),以辅助分析软件的性能和行为。
- OpenTracing - 是一套与平台无关、与厂商无关、与语言无关的追踪协议规范。官方提供多种语言的链路追踪库实现。目前官方已经不再维护。
链路追踪应用场景
- 故障快速定位:当用户反馈某次请求失败或耗时过长时,通过 TraceId 快速检索出整条调用链,定位到具体出问题的服务和方法,将排障时间从小时级缩短到分钟级。
- 性能瓶颈分析:通过分析调用链上每个 Span 的耗时占比,找出耗时最长的环节(如某次慢 SQL、某次远程调用),为性能优化提供数据支撑。
- 服务依赖梳理:自动生成服务调用拓扑图,帮助架构师直观了解系统内服务的依赖关系,发现不合理的调用(如循环依赖、跨机房调用)。
- A/B 测试与灰度发布:利用 Baggage 机制在链路上下文中传递实验标识,让链路上所有服务感知到当前请求所属的实验分组,实现全链路灰度。
- 异步消息链路追踪:将消息生产者和消费者的调用链串联起来,定位消息处理延迟和丢失问题。
- 微服务容量规划:基于链路数据统计各服务的调用量、依赖关系,为容量评估和资源扩缩容提供依据。
链路追踪最佳实践
案例 1:Spring Cloud Sleuth + Zipkin 接入链路追踪
通过 Spring Cloud Sleuth 自动为 HTTP、Feign、MQ 等调用注入追踪上下文,并将数据上报到 Zipkin Server 进行可视化展示。
Maven 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>application.yml 配置:
spring:
application:
name: order-service
sleuth:
sampler:
probability: 1.0 # 采样率,生产环境建议 0.1
propagation:
type: B3 # 使用 B3 协议传播
zipkin:
base-url: http://zipkin-server:9411
sender:
type: web # 通过 HTTP 上报,也可用 kafka/rabbitmq
service:
name: order-service启动服务后,发起一次跨服务调用,访问 http://zipkin-server:9411 即可在 Zipkin UI 中查看完整的调用链路。
案例 2:使用 SkyWalking Java Agent 实现无侵入追踪
SkyWalking 通过 Java Agent 字节码增强技术实现零代码侵入的链路追踪,适合对存量系统进行无改造接入。
下载 Agent:
# 下载并解压 SkyWalking Agent
wget https://archive.apache.org/dist/skywalking/java-agent/9.3.0/apache-skywalking-java-agent-9.3.0.tgz
tar -xzf apache-skywalking-java-agent-9.3.0.tgz启动应用时挂载 Agent:
java -javaagent:/path/to/skywalking-agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=oap-server:11800 \
-jar order-service.jarAgent 配置 agent.config(可选):
# 服务名称
agent.service_name=${SW_AGENT_NAME:order-service}
# OAP Server 地址
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:oap-server:11800}
# 采样率(-1 表示全采样,N 表示每 3 秒内采集 N 条)
agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE:-1}
# 忽略指定路径
agent.ignore_suffix=${SW_AGENT_IGNORE_SUFFIX:.jpg,.jpeg,.png,.gif,.css,.js,.ico}
# 链路最大跨度数(防止异常链路消耗过多资源)
agent.span_limit_per_segment=${SW_AGENT_SPAN_LIMIT:300}应用启动后无需修改任何业务代码,SkyWalking UI 即可自动展示调用链路、拓扑图和性能指标。
案例 3:在业务代码中自定义 Span 与 Baggage
当框架自动埋点无法覆盖业务关键逻辑时,可以通过 SDK 手动创建 Span,并通过 Baggage 在链路上传递业务数据。
import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
import io.micrometer.tracing.BaggageManager;
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
private final Tracer tracer;
private final BaggageManager baggageManager;
public PaymentService(Tracer tracer, BaggageManager baggageManager) {
this.tracer = tracer;
this.baggageManager = baggageManager;
}
public PaymentResult processPayment(PaymentRequest request) {
// 创建自定义 Span,记录关键业务方法
Span span = tracer.nextSpan().name("payment.process").start();
try (Tracer.SpanInScope scope = tracer.withSpan(span)) {
// 在 Baggage 中写入业务数据,会随链路向下游传播
baggageManager.getBaggage("user.id").ifPresent(b -> b.setValue(request.getUserId()));
baggageManager.getBaggage("ab.experiment").ifPresent(b -> b.setValue("variant_b"));
span.tag("payment.orderId", request.getOrderId());
span.tag("payment.amount", String.valueOf(request.getAmount()));
span.event("payment.start");
// 业务逻辑...
PaymentResult result = doPayment(request);
span.event("payment.end");
span.tag("payment.result", result.isSuccess() ? "success" : "fail");
return result;
} catch (Exception e) {
span.tag("error", "true");
span.tag("error.message", e.getMessage());
throw e;
} finally {
span.end();
}
}
}下游服务可以通过 baggageManager.getBaggage("ab.experiment").get().get() 获取上游写入的实验分组,实现全链路灰度。
链路追踪常见问题
问题 1:跨服务调用链路断裂,TraceId 丢失
问题描述:在 Zipkin 或 SkyWalking UI 中查看调用链时,发现本应串联的多个服务调用显示为独立的 Trace,TraceId 不一致,无法形成完整链路。
原因分析:
- 服务间调用未使用支持链路追踪的 HTTP Client(如使用了原生
HttpURLConnection且未手动传递追踪头)。 - 异步线程切换时,TraceContext 未正确传递(如
@Async、线程池、CompletableFuture)。 - 消息队列消费端未配置追踪支持,导致生产者到消费者的链路断裂。
- 追踪协议不一致(如上游用 B3、下游用 W3C TraceContext)。
解决方案:
- HTTP 调用确保使用 Sleuth 包装的 Client(如
RestTemplate、WebClient、Feign),它们会自动注入追踪头:
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
// Sleuth 会自动注入 TraceId 传播拦截器
return new RestTemplate();
}
}- 异步线程池中手动传递上下文(以 Sleuth 为例):
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
@Service
public class AsyncOrderService {
private final LazyTraceExecutor traceExecutor;
public AsyncOrderService(Tracer tracer, ThreadPoolTaskExecutor executor) {
this.traceExecutor = new LazyTraceExecutor(tracer, executor);
}
public void processAsync(String orderId) {
// 使用包装后的 Executor,TraceContext 自动传递
traceExecutor.execute(() -> {
// 这里的日志和调用仍属于同一个 Trace
doWork(orderId);
});
}
}- 消息队列消费端启用追踪(以 Spring Cloud Stream + Sleuth 为例):
spring:
cloud:
stream:
sleuth:
binder:
enabled: true # 启用消息链路追踪
kafka:
binder:
brokers: kafka:9092- 统一追踪传播协议,确保上下游服务使用相同协议(推荐 W3C TraceContext):
spring:
sleuth:
propagation:
type: W3C问题 2:链路追踪数据量过大,导致存储和性能压力
问题描述:接入链路追踪后,追踪数据快速增长,Zipkin/SkyWalking 后端存储(如 Elasticsearch、HBase)磁盘占用激增,查询变慢,甚至影响到业务应用的性能。
原因分析:
- 采样率设置过高(如 100% 采样),在海量请求下产生海量 Span 数据。
- 业务接口调用层级深、依赖多,单条 Trace 包含数百个 Span。
- 未对静态资源、健康检查等无意义请求进行过滤。
- 存储未配置合理的数据保留策略(TTL)。
解决方案:
- 合理设置采样率,生产环境通常 1%~10% 即可满足排障需求:
spring:
sleuth:
sampler:
probability: 0.1 # 采样 10%- 对关键接口强制采样,对无意义请求忽略:
import org.springframework.cloud.sleuth.SamplerProperties;
import org.springframework.cloud.sleuth.instrument.web.SkipPatternProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SamplingConfig {
@Bean
public SkipPatternProvider skipPatternProvider() {
return () -> "/actuator/.*|/health|/favicon\\.ico|/static/.*";
}
}- SkyWalking 配置数据保留周期(
application.yml):
core:
selector: ${SW_CORE:default}
default:
# 历史数据保留天数
recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:7}
# 指标数据保留天数
metricsDataTTL: ${SW_CORE_METRICS_DATA_TTL:14}- 使用采样策略插件(SkyWalking 支持按服务名、端点配置不同采样率),在 OAP Server 配置
trace-sampling-policy-settings:
{
"default": { "rate": 1000 },
"services": {
"critical-payment-service": { "rate": 10000 },
"low-priority-log-service": { "rate": 100 }
}
}问题 3:Baggage 数据泄露到外部系统导致安全风险
问题描述:在链路上下文(Baggage)中传递了用户敏感信息(如 userId、手机号),结果这些数据被透传到下游第三方服务或写入外部日志,造成数据泄露。
原因分析:
- Baggage 默认会随 HTTP 请求头透传到所有下游服务,包括第三方系统。
- 开发人员对 Baggage 的传播范围缺乏认知,将敏感业务数据放入 Baggage。
- 未对出境到第三方服务的请求头进行清洗。
解决方案:
不要在 Baggage 中存储敏感数据,敏感数据应通过专门的上下文机制(如 ThreadLocal + 服务间鉴权)传递。
在调用第三方服务前清洗追踪相关头:
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class ThirdPartyHeaderCleanInterceptor implements ClientHttpRequestInterceptor {
// 仅保留标准追踪头,剥离自定义 Baggage
private static final List<String> ALLOWED_HEADERS = List.of(
"traceparent", "tracestate", "x-b3-traceid", "x-b3-spanid"
);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.keySet().stream()
.filter(k -> k.toLowerCase().startsWith("baggage-") || k.equalsIgnoreCase("baggage"))
.toList()
.forEach(headers::remove);
return execution.execute(request, body);
}
}- 对 Baggage 进行加密或脱敏,仅传递必要标识:
// 不推荐:明文传递手机号
baggageManager.getBaggage("user.phone").ifPresent(b -> b.setValue("13800138000"));
// 推荐:传递不可逆的哈希值或脱敏后的标识
baggageManager.getBaggage("user.hash").ifPresent(b -> b.setValue(sha256("13800138000")));参考资料
- 标准化
- OpenTelemetry - OpenCensus 和 OpenTracing 两个项目的合并
- OpenTracing - OpenTracing 是一套与平台无关、与厂商无关、与语言无关的追踪协议规范
- 文章
- Dapper 论文 - 即:Dapper, a Large-Scale Distributed Systems Tracing Infrastructure
- Dapper 论文翻译
- 凤凰架构-链路追踪