用链路追踪定位微服务慢请求,核心思路是依靠traceId将一次请求经过的所有服务节点串联起来,通过分析各节点的时间消耗,快速找到耗时最长的故障点。
慢请求为什么在微服务里特别难以捉摸
在单体应用时代,慢请求的排查相对简单,一次请求从入口到出口都在一个进程内,你只需要盯着数据库慢查询日志,或者检查一下Redis响应时间,基本就能定位问题,但到了微服务架构下,事情变得复杂得多。
一个用户请求,往往要经过API网关、认证服务、用户服务、订单服务、库存服务等五六个甚至十几个节点,任何一个节点慢,整个请求就会慢,更让人头疼的是,这种“慢”往往是传递性的,比如数据库有一条慢SQL,导致订单服务接口响应时间飙升,但此时订单服务的CPU和内存看似正常,因为线程都在等待数据库返回,如果你单看订单服务的监控面板,可能什么破绽都发现不了。
行业共识认为,微服务排障最大的痛点在于“看得到现象,找不到源头”,监控系统能告诉你哪个接口慢了,但不能告诉你为什么慢,是下游服务慢了?还是网络抖动?还是线程池被打满了?这时候,链路追踪就成了唯一能还原完整真相的工具。
链路追踪数据里藏着定位慢请求的关键
链路追踪(Distributed Tracing)之所以能解决这个问题,是因为它为每个请求生成了一个全局唯一的traceId(追踪标识),并且给每个服务内部的调用片段取了一个span(跨度)。
理解traceId和span的作用
可以把一次微服务调用想象成一趟快递运输。
- traceId 就是快递单号,它贯穿全程,从收件、分拣、运输、派送,每个环节都会记录这个单号。
- span 则是每个环节的操作记录,装载上车”是一个span,“到达中转站”是另一个span,每个span都记录了自己的开始时间、结束时间、状态和花费的时间。
当你发现某个接口响应慢时,通过traceId去链路追踪系统里一搜,就能看到这趟“运输”在哪个环节卡住了,是“装载上车”环节(服务A调用服务B)耗时300毫秒,还是“到达中转站”环节(服务B处理逻辑)耗时2秒。
如何通过时间戳识别同级并行调用
定位慢请求还有个小技巧,就是看span之间的父子结构和时间重叠关系。
如果一个服务需要同时调用三个下游服务,正常情况下,三个调用是并行的,体现在时间轴上会有重叠,如果链路追踪图显示三个调用是

串行的(前一个结束,后一个才开始),说明代码里可能用错了调用方式(比如在for循环里依次调用了三个服务),这本身就是一种性能瓶颈。
通过critical path(关键路径)判断也很有效,关键路径是指整个调用链中耗时最长的那条链路,相对次要的调用可以先不看,只聚焦于关键路径上的关键span,比如网关到订单服务花了800毫秒,订单服务到数据库花了600毫秒,那目标就很明确:去查数据库实例的问题,而不是去翻网关日志。
从traceId到数据落库的定位执行步骤
拿到traceId怎么操作,这是最核心的实操部分,不同公司的链路追踪系统不同,但思路和方法是通用的。
-
第一步:从入口提取traceId
如果你的服务用了如Spring Cloud Sleuth或SkyWalking的Agent,请求头里会自动携带traceId,最常见的方式是,从网关的访问日志(Access Log)里找到慢请求的traceId,或者,通过浏览器开发者工具(F12),在Network里找到那个耗时长的接口,复制响应头中标记为traceId或X-B3-TraceId的字段值。 -
第二步:在查询界面检索traceId
打开公司用的链路追踪平台(如SkyWalking UI、Jaeger UI或Zipkin UI),在搜索框粘贴traceId,系统会展示这棵Span调用树,从入口到最后退出,一个都不缺。 -
第三步:按时间倒序寻找“自耗时”
在页面左侧的Span列表里,查看每个span的自耗时(self-duration,即不包括子span的耗时),如果某个span的自耗时占总耗时的比例较大,比如总耗时2秒,这个span自己占了1.8秒,那问题几乎就锁定了,如果所有span的耗时都不长,但总耗时很长,那么问题很可能出在网络传输或线程等待上。 -
第四步:关联日志深挖根因
锁定了慢的span后,还要看它记录的异常堆栈或业务日志,以Elastic APM为例,它能将慢查询的SQL语句直接展示在span详情里,你一眼就能看到是不是SQL忘加了索引。
给慢请求“做体检”的实操路径与工具经验
知道了怎么定位,我们还要聊下在不同工具里的具体操作,毕竟工具用得不熟会浪费大量时间。
用SkyWalking定位慢请求的典型流程

SkyWalking在定位慢请求方面体验较流畅,执行逻辑很直观。实测路径: 打开SkyWalking UI,点击“追踪”页签,在右侧阈值框内输入2000(即慢请求阈值,单位毫秒),点击搜索。
这时UI会列出所有耗时超过2秒的Trace数据,不用一个个点开,优先看列表里的“响应时间”列,从大到小排,点开一个,页面左上角的“慢查询日志”标签会直接显示这条链路里数据库的执行语句,这是排查效率较高的入口,对定位MySQL慢查询场景,这个功能比手工去数据库跑show full processlist更高效,因为直接和业务请求关联起来了。
不同场景下的链路追踪工具对比
至于选型,需要看具体业务场景,如果公司运维能力一般,首选SkyWalking,因为它无侵入且自带UI,Java探针无需改动业务代码,接入成本较低,相比之下,Zipkin因为上报机制依赖代码主动集成,在维护成本上更依赖团队配合。
| 对比维度 | SkyWalking | Zipkin | Jaeger |
|---|---|---|---|
| 数据存储 | H2/MySQL/Elasticsearch | Cassandra/Elasticsearch | Cassandra/Elasticsearch |
| 代码侵入性 | 无侵入(JavaAgent) | 侵入性强(需提供数据) | 侵入性中等(提供SDK) |
| 告警能力 | 内置告警规则引擎 | 需结合外部工具 | 需结合外部工具 |
| 埋点语言 | 多语言,Java支持较好 | 多语言 | Go/Python支持较好 |
慢请求定位方法的深度视角
当你开始频繁面对慢请求,且工具已经用得很熟时,你会发现链路追踪只是一个放大镜,它帮你把问题定位到了某个特定代码块,但更核心的排查动作,依然要靠时间去沉淀,定位到慢请求后,后面才是真正的硬仗。

大多数情况下,慢请求的元凶逃不出以下四类:
- 数据库瓶颈:SQL没走索引、锁表、连接池耗尽。
- 依赖超时:下游服务响应慢,且上游服务未设置合理的超时时间,导致线程被阻塞。
- 资源竞争:Java中的堆内存频繁GC,或者线程池队列积压。
- 网络抖动:容器化环境下,节点之间网络IO过高或DNS解析缓慢。
关于网络,有一种情况可以让链路追踪系统“哑火”:即所有的Span耗时都正常,总耗时却异常地长,这种“时间黑洞”通常是因为服务线程在等待逻辑代码执行,比如乐观锁自旋,或者RPC连接从池中获取时阻塞,链路追踪没法覆盖这种情况,你需要借助JVM线程转储(jstack)来查看线程状态,确认是否有死锁或阻塞。
Q&A:关于链路追踪定位慢请求的常见疑问
Q1:链路追踪和APM是一回事吗?定位慢请求哪个更高效?
它们有交集,但不完全对等,APM(应用性能监控)是更宽泛的概念,包含了RUM(真实用户监控)、拓扑发现,链路追踪是APM的核心组件,但APM还包含了基础设施监控,定位慢请求,链路追踪的数据更精准,它能够直接给出完整调用链的事件序列,而纯APM监控可能只提供概览和数据聚合,无法下钻到单次请求的调用链。
Q2:在没有链路追踪配置的传统F5/负载均衡架构下,定位慢请求有替代方案吗?
可以先使用动态APM注入工具,或者临时利用日志中手动拼接并发请求的唯一标识,另一种常见做法是,选取慢请求发生的时间窗口,登录各业务节点服务器,人工拉取该时间段的错误日志和线程快照,通过时间戳拼接来分析,这种方式的成本较高,且要求时间同步精准(通过NTP同步),定位速度会慢一些。
Q3:定位到某个微服务有慢请求,却发现其CPU和内存占用率都很低,接下来该怎么查?
这大概率是下游依赖或外部调用阻塞所致,需要重点检查该服务的连接池状态(关注线程数、活跃连接数、队列等待值),以及对外部中间件的请求耗时,一个值得检查的环节是DNS解析,服务启动时使用内网域名会导致部分请求在获取连接时等待,执行nslookup或dig命令验证解析耗时是个直接的排查步骤。