1. 三个必须上链路追踪的典型场景:别再等故障发生了才后悔
微服务架构走到第六个年头,我最大的一个醒悟是:链路追踪不是给领导看的监控大屏,也不是技术博客里用来炫耀的架构图,而是你凌晨三点被叫起来排查故障时唯一能救命的线索图。
先说一个真实经历。去年我们团队接手了一套内部订单系统,六个微服务互相调用,用户反馈“下单偶尔很慢,有时候要转十几秒才出结果”。当时第一反应是查数据库慢查询,DBA把慢日志翻了个底朝天,没发现异常;再查网关日志,发现调用下游服务时耗时飘忽不定,但就是定位不到具体是哪一环出了问题。折腾了两天,最后靠手工在业务代码里加日志、反复压测,才勉强锁定到某个服务的Redis连接池配置不合理。这件事之后我下定决心:链路追踪必须尽快落地。
链路追踪解决的三个核心问题,实践下来感受特别深。第一个是跨服务故障定责。微服务架构下,一次用户请求可能经过网关、鉴权、订单、库存、支付五六个服务,任何一个环节挂了或者慢了,用户感知的都是“系统卡了”。没有链路追踪,你只能靠各部门互相甩锅;有了它,一条TraceId就能把整条调用链串起来,一眼看出瓶颈在哪个服务、哪次调用、耗时多少。
第二个是性能瓶颈定位。很多性能问题不是某一个服务慢,而是服务间调用的网络开销、序列化开销、连接池等待叠加出来的。举个典型例子:服务A调用服务B,B又调用C,C本身只要5毫秒,但B到C的HTTP连接建立需要50毫秒——单看每个服务的平均响应时间都正常,连起来就慢得离谱。链路追踪能把每一跳的耗时拆开,这种隐藏的“链路叠加延迟”立刻原形毕露。
第三个是分布式事务排查。在微服务架构里,没有全局事务,只有最终一致性。一旦某个异步消息丢了、某个回调没触发,数据就对不上了。链路追踪至少能告诉你:这条消息到底有没有发出去、有没有被消费、卡在了哪个环节。
所以这篇文章不是讲概念的,而是结合我线上环境的真实使用经验,把 SkyWalking 和 Zipkin 这两套主流方案从架构差异、选型思路、性能调优到排障实战完整串一遍,给已经准备上链路追踪、或者正在 SkyWalking 和 Zipkin 之间犹豫的团队一个可落地的参考。
2. SkyWalking 与 Zipkin 的架构分水岭:探针、存储与数据模型
很多团队选型时只看 GitHub Star 数和社区活跃度,但真正决定后续使用体验的,是两者的架构设计差异。这一节我把它们拆开揉碎讲清楚。
2.1 探针机制:Java Agent 的“无侵入” vs Brave 的“半侵入”
SkyWalking 的核心优势是无侵入接入。它的探针基于 Java Agent 字节码增强技术,启动时通过-javaagent参数挂载,运行时自动拦截 HTTP 框架、数据库驱动、消息队列客户端的调用,自动生成 Trace 数据。对业务代码零改动,这对存量系统尤其友好——你不用为了接入链路追踪去改每一处 Feign 调用、每一条 JDBC 连接。
Zipkin 采用的是 Brave 埋点方式。严格来说 Brave 也提供了自动装配的 starter 依赖(比如brave-instrumentation-spring-web),但在某些自定义线程池、异步场景、非主流框架下,你还是得手动在业务代码里创建 Span、手动注入 TraceContext。如果团队里有多个语言栈,Java 之外的 SDK 往往需要更细致的埋点配置。
这里有个容易踩的坑:SkyWalking 的探针虽然无侵入,但它是“黑盒”的。一旦某些框架版本过新、或者你用了自研的通信协议,探针可能识别不了,表现是链路断了、数据缺失,排查起来反而麻烦。Zipkin 的埋点方式是“白盒”的,代码里能明确看到 Span 的起止位置,逻辑透明,但代价是侵入性。
2.2 数据模型:Trace 单维度 vs Trace + Topology + Metrics 三维度
Zipkin 的核心数据模型是 Span 和 Trace,它把一次请求拆成多个 Span,通过 TraceId 关联起来。Zipkin UI 里能清晰地看到调用链的时间轴,但这个时间轴是“线性”的——它擅长回答“这次请求经历了哪些服务,各自耗时多少”,但不擅长回答“订单服务过去一小时的整体健康状况如何”。
SkyWalking 的数据模型则做了明显升级。它不仅有 Trace 数据,还内置了服务拓扑图(Topology)、服务实例监控(Metrics)、端点监控(Endpoint Analysis)。这意味着一套 SkyWalking 同时兼顾了链路追踪和 APM 监控。打开拓扑图,你能看到订单服务调用库存服务、支付服务的实时流量关系,哪个服务是扇入扇出热点、哪个服务实例响应时间飙高,一目了然。
实际使用体验差异很大。用 Zipkin 查一条慢请求,你得知道请求的大概时间点,然后去搜索 Trace;用 SkyWalking 可以直接从服务拓扑图点进某个端点,看它的响应时间走势曲线,再钻取到具体的慢 Trace。前者是“点查”,后者是“面查”。
2.3 存储选型:ES 家族 vs H2/ES/BanyanDB
存储是链路追踪系统最容易忽略却又最要命的环节。Zipkin 官方支持的存储包括内存、MySQL、Elasticsearch、Cassandra,生产环境绝大多数选 Elasticsearch。ES 存储的好处是查询灵活、支持复杂的 TraceId 检索,缺点是运维成本高——需要自己管理索引生命周期,数据量大时要考虑分片策略,否则查询会越来越慢。
SkyWalking 的存储经历了几个阶段:早期以 ES 为主,后来推出了自己的时序数据库 BanyanDB。我在生产环境用的是 ES,但 BanyanDB 在减少存储容量、提升写入性能方面确实有优势,尤其适合数据量很大的场景。SkyWalking 在存储层做了一个不错的抽象:你不需要关心底层 Trace 数据的具体存储结构,只需要配置存储类型、TTL 清理策略,系统会自动完成数据的写入和过期清理。
如果让我给两条路线下个简单结论:纯 Java 技术栈、业务复杂度高、团队运维人力有限的,选 SkyWalking;异构系统多、需要深度定制链路数据的,选 Zipkin。这个结论背后不是谁比谁先进的问题,而是架构设计理念的差异决定的。
3. 选型不是看热度,是看你的团队能喂饱哪套体系
每次技术选型讨论,总有人拿 SkyWalking 和 Zipkin 比功能列表,比到最后发现功能几乎都对得上,然后开始比 UI 颜值、比文档完善度。这些不是不重要,但真正的选型决策应该围绕三件事:你的技术栈构成、你的数据规模、你团队愿意花多少精力去维护这套系统。
3.1 Java 技术栈 vs 多语言异构
先看技术栈占比。如果你们的微服务框架是 Spring Cloud Alibaba 那套(Nacos 注册中心、OpenFeign 调用、Sentinel 限流),那么 SkyWalking 能发挥最大的优势——它的探针原生支持 Dubbo、Spring Cloud Gateway、Sentinel 等组件,链路数据里甚至能直接关联上 Sentinel 的限流日志。同时 SkyWalking 提供了 Java、Go、Python、Node.js、PHP 等语言的探针,但非 Java 语言的探针成熟度明显低于 Java Agent。
Zipkin 的优势在于多语言支持生态更均匀,Brave 是 Java 的推荐库,OpenTracing 为多种语言提供了一致的埋点 API。如果你的系统是“Go 写网关 + Java 写业务 + Python 写算法服务”这种组合,Zipkin 的接入方式会更统一。
3.2 数据量大小与查询模式
先问自己一个问题:线上一天的 Trace 数据量是多少?这个问题很多团队答不上来,因为没上链路追踪之前根本无法估算。我给出一个粗略算法:假设每天订单量 20 万笔,每笔订单平均触发 15 次服务调用,那么一天的 Trace 数据就是 300 万条。每条 Trace 平均 8 个 Span,就是 2400 万个 Span。
在千万级 Span 日增量的场景下,Zipkin + ES 的存储成本会很高,查询也会变慢。SkyWalking 在存储层做了大量优化(比如 span 数据聚合、指标预计算),BanyanDB 更是专门为这种时序+追踪数据设计的,长期运行更省心。如果你们的数据量预估在百万级 Span 每天,Zipkin 完全够用;如果到千万级以上,我更推荐 SkyWalking。
还有查询模式的问题。研发日常排障最喜欢问的问题是:这条订单的 TraceId 给我,我去查链路。Zipkin 对 TraceId 的查询是最快的,因为它原生支持按 TraceId 精确定位。SkyWalking 也能按 TraceId 查,但 UI 层面更偏爱“先看拓扑、再下钻端点、最后看 Trace”这种路径。
3.3 运维成本与团队技能树
链路追踪系统本身也是一个分布式系统,它同样需要部署、扩容、调优。Zipkin 的官方集群模式依赖 Cassandra 做存储,但大多数团队为了省事直接用 ES,这样你就需要同时维护 Zipkin Server 和 ES 集群。SkyWalking 的后端(OAP Server)可以独立部署,支持水平扩容,存储用 ES 或 BanyanDB,本身就是一套完整的 APM 系统。
从团队技能树来看,如果你们团队已经有熟悉 ES 运维的人,Zipkin 的方案更顺手;如果团队更希望“开箱即用”、不想花太多精力维护链路系统本身的组件,SkyWalking 的 OAP + UI + Agent 一体化设计明显更省心。
我用一张表把核心差异列出来,方便做决策时直接对照:
| 对比维度 | SkyWalking | Zipkin |
|---|---|---|
| 接入方式 | Java Agent 无侵入 | Brave 埋点,部分自动装配 |
| 数据模型 | Trace + Topology + Metrics | Trace + Span |
| 多语言支持 | Java 强,其他语言可用 | 多语言生态相对均匀 |
| 存储方案 | ES / BanyanDB / MySQL | 内存 / MySQL / ES / Cassandra |
| UI 易用性 | 拓扑图、端点分析、告警一体 | 时间轴清晰,但功能较单一 |
| 运维复杂度 | 一体化部署,OAP + UI + Agent | 需自行组装 Server + 存储 |
| 社区活跃度 | 高,中文社区活跃 | 高,老牌稳定 |
3.4 我的实践结论
我们当时的场景是:纯 Java 技术栈、Spring Cloud Alibaba 全家桶、日 Trace 量大约 1000 万 Span。最终选了 SkyWalking,理由也很直白:不想在链路追踪系统本身投入太多运维精力,同时需要拓扑图和指标数据辅助日常监控。如果你们有深度定制链路数据的诉求,比如要往 Span 里塞自定义业务字段、要做精细的采样策略,Zipkin + Brave 的方案会更加灵活。
4. 性能优化:从采样策略到存储写入的全链路调优
链路追踪系统上线后,最容易被吐槽的问题就是“太吃性能”。确实,任何一个链路追踪工具都有开销——探针拦截、上下文传递、Span 生成、异步上报,每一步都有成本。但真实生产环境中,我实测 SkyWalking 的 Agent 对吞吐量的影响在 5% 到 10% 之间,Zipkin 的 Brave 埋点影响更小,大概 3% 到 5%。关键在于你会不会调。
4.1 采样策略:不是所有链路都值得全量记录
这是优化空间最大的一步。很多团队刚开始上链路追踪时,为了追求“完整数据”,直接全量采集,结果一个月下来 ES 存储爆掉,查询慢得怀疑人生。
正确的做法是分层采样。核心交易链路(下单、支付、退款)全量采样,因为这部分数据量可控、且出问题影响最大;非核心链路(查询类、报表类、异步通知类)按 10% 采样。SkyWalking 的采样配置在 agent 配置文件中:
# agent/config/agent.config # 采样率配置,10000 表示 100% agent.sample_n_per_3_secs=10000 # 忽略某些端点的追踪,比如健康检查、监控上报 agent.ignore_suffix=,favicon.ico,.js,.css,.html,.map # 忽略特定 URL 或特定服务 agent.ignore_path=/health,/metrics,/actuator/healthZipkin 的采样则通过 Brave 的Sampler实现:
@Bean public Sampler zipkinSampler() { // 10% 采样率 return Sampler.create(0.1F); }我实际调优后的效果很明显:打开采样率后,ES 的索引写入压力下降了约 60%,查询响应时间从秒级降到了百毫秒级。但要注意,采样率调低后,排障时经常遇到“这条 Trace 没被采样到”的尴尬情况,所以我的策略是:核心交易请求通过业务字段强制标记,即使采样率是 10%,这笔订单的 Trace 必须记录。SkyWalking 支持在业务代码里通过 SkyWalking TraceContext 强制上报,Zipkin 则可以使用Span.current()或自定义Sampler来处理。
4.2 Agent 端调优:别忽略 JVM 参数与异步场景
SkyWalking Agent 默认会拦截很多组件。如果你们有一些高频调用但不需要追踪的路径(比如定期拉取配置、健康检查、内部心跳),可以在agent.config里通过agent.ignore_path把它们排除掉,减少无意义的 Span 生成。
Agent 对 JVM 的参数也有影响。SkyWalking 的 Java Agent 字节码增强是有开销的,但实测下来对 GC 的影响可控。不过有一个坑特别值得提醒:如果你们的服务使用了 Spring Boot 的 Fat JAR 启动方式,记得把 SkyWalking Agent 的-javaagent参数放在-jar之前。很多人一开始把参数顺序写反,导致 Agent 根本没有生效,链路数据一直是空的。
# 正确写法 java -javaagent:/path/to/skywalking-agent.jar -jar your-app.jar # 错误写法(Agent 不生效) java -jar your-app.jar -javaagent:/path/to/skywalking-agent.jar异步线程场景也是容易丢数据的地方。SkyWalking 对ExecutorService、@Async注解的方法有自动增强,但如果你是在自定义线程池里手动提交任务,需要确保线程池的上下文传递。SkylWalking 提供了TraceContext和 Runnable/Callable 包装器:
ExecutorService executor = Executors.newFixedThreadPool(8); Runnable task = () -> { // 异步处理的业务逻辑 doSomething(); }; // 使用 SkyWalking 提供的 Runnable 包装器 executor.execute(TraceContext.wrap(task));Zipkin 的处理思路类似,Brave 提供了Tracing.currentTraceContext().wrap(task)。异步场景如果不处理,链路会在线程切换的地方断掉,后续排障时看到的就是一条残缺链路。
4.3 存储调优:ES 索引生命周期与写入性能
不管是 SkyWalking 还是 Zipkin,生产环境的大多数性能问题最终都出在存储层。ES 写入性能跟不上,会导致链路数据积压,OAP Server 或 Zipkin Server 的内存暴涨,反过来拖垮整个链路系统。
SkyWalking 的 ES 存储优化有几个关键参数:索引模板的分片数和副本数。默认配置下,SkyWalking 为每个索引创建 5 个分片、1 个副本,如果你的数据量不大,可以调低分片数:
# SkyWalking OAP 的 application.yaml storage: selector: ${SW_STORAGE:elasticsearch} elasticsearch: namespace: ${SW_NAMESPACE:""} clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:localhost:9200} # 分片数量 indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:3} # 副本数量 indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:1} # 数据保留天数 recordDataTTL: ${SW_STORAGE_ES_RECORD_DATA_TTL:7} # 指标保留天数 minuteMetricsDataTTL: ${SW_STORAGE_ES_MINUTE_METRICS_DATA_TTL:7} hourMetricsDataTTL: ${SW_STORAGE_ES_HOUR_METRICS_DATA_TTL:30} dayMetricsDataTTL: ${SW_STORAGE_ES_DAY_METRICS_DATA_TTL:90} monthMetricsDataTTL: ${SW_STORAGE_ES_MONTH_METRICS_DATA_TTL:365}注意默认的 TTL 配置,Segment 记录只保留 7 天。如果你需要排查比较久远的链路问题,记得把recordDataTTL调大,但也要同步考虑磁盘容量。
Zipkin 的 ES 存储需要你自己管理索引模板。Zipkin 官方提供了依赖表信息,但索引生命周期策略需要自己配。我的建议是启用 ES 的 Index Lifecycle Management(ILM),设定定时索引滚动:
{ "policy": { "phases": { "hot": { "actions": { "rollover": { "max_size": "50GB", "max_age": "1d" } } }, "delete": { "min_age": "7d", "actions": { "delete": {} } } } } }重点提醒:ES 的refresh_interval默认 1 秒,如果写入压力大,可以调大到 30 秒,写入性能翻倍,但对查询实时性有一定影响。
4.4 上报方式:gRPC vs HTTP,少了这个关键选择白忙一场
SkyWalking Agent 上报数据默认走 gRPC,这是性能最好的方式,也是官方推荐的生产环境方式。但 gRPC 的端口(11800)和 UI 的 HTTP 端口(12800)要分清,防火墙策略不要搞混。
Zipkin 的 Collector 接收数据支持 HTTP 和 Kafka 两种方式。数据量大的时候,我强烈建议走 Kafka 中转——Zipkin Collector 从 Kafka 消费消息写入 ES,避免大量 HTTP 请求直接打到 Collector 上造成阻塞。Brave 的 HTTP 上报是同步阻塞的,如果上报接口响应慢,反而会拖慢业务线程。用 Kafka 异步解耦后,业务端的延迟影响几乎可以忽略。
这部分还有一个容易忽略的点:链路数据的压缩。SkyWalking 和 Zipkin 默认都开启了数据压缩,但确认一下你用的是 gzip 还是 snappy。我实测 gzip 对 JSON 数据压缩率在 60% 到 70%,能有效降低网络传输开销。
4.5 网关层优化:别让链路追踪成为高并发下的瓶颈
如果你的网关层(Spring Cloud Gateway、Zuul)也接了链路追踪,要特别注意它的性能。网关是所有流量的入口,Agent 的拦截逻辑在高并发下会被无限放大。我的建议是:
- 网关层采样率调低,比如 5% 或 10%,因为网关层主要关注的是路由转发和限流信息,业务链路细节由下游服务记录。
- 关闭非核心过滤器链路的 Track,比如静态资源路由、健康检查路径。
- 如果网关本身有性能瓶颈,优先排查网关的内存和 GC 情况,链路追踪 Agent 在这个场景下不是主要矛盾。
5. 链路追踪上线后的排障实战:先定位业务,再定位系统
工具落地之后,真正的考验才开始。我总结了一套自己常用的排障思路,按这套思路,绝大多数问题能在 10 分钟内定位到根因。
5.1 从拓扑图发现“流量异常聚集点”
打开 SkyWalking 的拓扑图,第一件事不是看响应时间,而是看流量关系。某个服务的扇入数突然暴增,大概率是被上游调用了太多次——这往往是代码里出现了重复调用、循环调用或者 Feign 重试机制导致的。我在生产环境遇到过一个问题:一个查询接口的 P99 延迟从 200ms 涨到 2s,拓扑图显示某个服务的被调用次数翻了三倍,顺着调用链下钻,发现是上游服务在 for 循环里调用了三次查询接口。
拓扑图的另一个用法是观察调用方向的合理性。如果出现了一个不该出现的调用关系,比如订单服务直接调用了用户服务(而架构上应该走网关转发),那说明有人写了绕过网关的跨服务调用,长此以往会破坏整个微服务架构的边界。这类问题藏得很深,没有拓扑图根本发现不了。
5.2 慢调用链路拆解:每一跳的耗时都别放过
当一条请求响应变慢时,打开 Trace 时间轴,把每一跳的耗时拆出来。以下是我在 Zipkin 的 UI 里看到的典型问题模式:
- 服务 A 调用服务 B 的耗时高,但服务 B 自己的 Span 耗时很低—— 说明问题出在 A 和 B 之间的网络传输、序列化或连接池获取上。最常见的是 HTTP 连接池配置过小,导致大量线程在等待连接。
- 服务 B 的 Span 耗时高,且数据库 Span 占比最大—— 查 SQL 慢查询、锁等待、连接池瓶颈。
- 服务 B 的 Span 耗时高,但子 Span 都很快—— 大概率是服务 B 的某个非链路监控逻辑耗时,比如本地缓存失效后重新加载、Java 的 GC 暂停。
还有一个经常被忽略的点:时间漂移。如果服务器的时钟没有做 NTP 同步,Trace 时间轴会出现“子 Span 耗时大于父 Span 耗时”的诡异现象。链路追踪系统依赖各服务器的系统时间计算耗时,时钟不同步会导致数据完全不可信。我就在一次排障中发现某个新扩容的容器没有挂 NTP,所有经过该容器的链路都显示耗时异常。
5.3 链路数据缺失的排查链路:Agent、网络、存储三线并查
链路数据缺失是最常见的排障场景。现象是:某些服务的 Trace 有,某些服务的 Trace 没有;或者只有入口 Trace,没有下游调用。
我的排查顺序是:
- 确认 Agent 是否生效。看服务启动日志里有没有 SkyWalking Agent 的输出,比如
skywalking agent started。没有的话,检查-javaagent参数是否写对、agent 路径是否正确、挂载方式是否被公司内部的启动脚本覆盖了。 - 确认上报网络是否通畅。SkyWalking Agent 需要访问 OAP 的 gRPC 端口(默认 11800),Zipkin 的 Reporter 需要访问 Collector 的 HTTP 端口(默认 9411)。用
telnet或nc测试下端口连通性,很多故障其实是防火墙策略导致的。 - 确认采样策略没把链路丢了。检查配置的采样率是不是太低,尤其是刚调完采样率后最容易出这种问题。
- 确认存储是否写入成功。在 ES 里查一下最新索引的文档数量,看看有没有 SPI 级别的错误日志。如果 OAP 日志里有 ES 写入报错,优先排查 ES 的磁盘空间和分片状态。
这套排查链路我用过很多次,90% 的数据缺失问题都能在十几分钟内解决。剩下 10% 是探针版本和框架版本不兼容导致的,这类问题处理起来比较麻烦,常见解法是升级 Agent 版本或手动埋点绕过。
5.4 从 Trace 关联到日志:TraceId 是排障的粘合剂
链路追踪只告诉你“哪一步慢了、哪一步挂了”,但告诉你“为什么慢、为什么挂”的通常是日志。所以上线链路追踪的必备配套动作是:把 TraceId 打印到应用日志里。
SkyWalking 的做法是在项目的 logback 配置里加一个 TraceIdPatternLogbackLayout:
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} - %msg%n</pattern> </layout> </encoder> </appender>这样日志里每一行都会带上 TraceId。排障时根据链路的 TraceId 去日志平台搜索,就能把一次请求的完整生命周期(链路时序 + 业务日志 + 异常堆栈)拼凑起来。这个步骤看起来不起眼,但实际排障效率提升一倍不止。
Zipkin 的 Brave 则通过MDC方式注入 TraceId:
tracing.currentTraceContext().get().traceId()加进日志 pattern 即可。排障时用同样的方式关联日志和链路数据。
5.5 长期维护:链路追踪系统本身也要监控起来
链路追踪系统是给业务排障的,但链路追踪系统自己也会出故障。我的建议是至少监控以下四个指标:
- OAP/Collector 的 JVM 指标。包括堆内存、GC 次数、CPU 使用率。OAP 如果 Full GC 频繁,往往意味着 ES 写入阻塞或 Kafka 消费积压。
- Agent 上报的 Span 数量趋势。如果某天 Span 数量突然下降,大概率是部分服务的 Agent 出了问题或上报链路断了。
- ES 集群的磁盘使用率和写入 QPS。链路数据是典型的写多读少,ES 的写入能力决定了链路系统的天花板。注意给 ES 留足磁盘余量,定期清理历史索引。
- 链路系统的告警配置。SkyWalking 自带告警规则,可以配置服务响应时间超过阈值就告警;Zipkin 则需要配合 Prometheus 或自建告警。链路追踪系统本身如果挂了,业务还在跑,但就是什么都查不到——这是最尴尬的巡检盲区。
写在最后:两个小建议
链路追踪不是一锤子买卖,上线只是开始。我自己经历过的教训是,一开始全量接入所有服务,结果数据量暴增,存储成本居高不下,团队反而失去了看链路的耐心。后来我改为按核心交易链路优先接入、逐步扩展的策略,效果反而更好。
另一个建议是:把链路追踪和日常的发布变更结合起来看。每次发版后,对比一下链路追踪里的关键服务响应时间、拓扑关系变化,很多时候线上问题的根因都能提前暴露出来,不用等用户投诉了再被动排查。链路追踪这个工具,用得越深,能挖出来的东西越多。