Skip to content

横跨十几个分布式服务的慢请求要如何排查? ​

  • 标签:后端、场景题

🏛 业务架构师 ​

横跨十几个分布式服务的慢请求排查,单机排查手段(jstack/jstat)已经不够用,核心是全链路可观测性 + 分段归因。我按"有 Trace"和"没 Trace"两种情况答——后者其实是大多数公司的真实起点。

有全链路追踪(标准解法)

  1. 从慢请求样本取 traceId,打开完整调用链瀑布图
  2. 看 span 耗时分布,最长的那段就是嫌疑犯:慢 SQL、下游 RPC、MQ 投递、锁等待
  3. 看"时钟对不齐"的部分:服务间网络耗时(跨机房调用)、序列化
  4. 统计归因而非个案:拉一周慢请求的 trace 做聚合分析,看慢的共性(都在依赖 X 时慢?都在高峰期慢?),比追单条 trace 更接近根因
  5. 常见结论排序:慢 SQL > 某下游服务抖动 > 批量接口循环调用(N+1 RPC)> 序列化大报文 > GC

没有 Trace(先补观测再谈排查)

  • 应急:在网关和各服务入口加 traceId 透传(MDC + header 传递),日志按 traceId 聚合,手工拼链路
  • 上线 OpenTelemetry/SkyWalking 是一次性投入换永久排查能力,十几个服务的规模没有理由裸奔——这是我到任何一个团队先推的第一件事

架构师视角的收尾

慢请求治理的本质是依赖治理:每个服务声明自己的 P99 SLO,聚合接口对依赖设定"超时预算",超预算的依赖要么异步化要么降级。排查工具解决"这一次为什么慢",SLO 和预算机制解决"慢不再传导、不再放大"——后者才是架构师该交付的东西。另外大促前必须做全链路压测,把慢的依赖提前在压测环境暴露,而不是等生产用户帮你发现。

🏗 系统架构师 ​

跨十几个服务的慢请求,靠逐个服务看日志是低效的,我的方法论是全链路追踪 + 分段定位 + 验证归因。

第一步:全链路 Trace 定位慢在哪一段 前提是链路有 TraceId 全透传(OpenTelemetry/SkyWalking)。拿到慢请求的 TraceId,在追踪系统里看瀑布图:整个 1s 里哪个 Span 占了 800ms。没有 trace 体系就先补日志打点:每个服务入口/出口记 traceId + 耗时,网关层记录各跳。十几个服务的人工排查成本,决定了可观测性建设是前提而不是选项。

第二步:对慢的 Span 分类归因 慢只有几种:慢 SQL(看执行计划)、慢 RPC(下游又慢在哪,递归下钻)、锁等待/GC 停顿(线程栈 BLOCKED、GC 日志)、资源排队(连接池、线程池满,看 active/idle 水位)、序列化/大报文(payload 几 MB 的 JSON)。

第三步:并行调用分析 十几个服务的调用里经常藏着串行化的并行机会:A→B→C 串行各 300ms,若 B、C 无依赖可并行降到 300ms。Trace 图上串行长链是首要优化点。

第四步:常见系统性根因清单

  • 超时配置不合理导致"等满才失败"
  • 某个公共服务被所有链路依赖,形成隐性单点
  • 网络问题:跨机房调用、DNS 解析慢、TCP 建连开销(缺连接池复用)
  • 日志同步刷盘、拦截器里做重活

输出:把结论落到监控上——每服务 P99 分位数 + 接口级耗时分解常态化,下次慢请求 5 分钟出定位报告而不是 5 小时。

💻 全栈开发专家 ​

横跨十几个服务的慢请求,靠肉眼翻日志是不可能排查的,必须靠全链路追踪 + traceId 串联。

标准排查流程

  1. 拿 traceId:从网关日志或用户反馈(页面响应头里塞 traceId 返回)拿到慢请求 ID。
  2. 看 trace 瀑布图:SkyWalking 查这个 trace,一眼看到:
    • 总耗时分布:网关 → 服务 A → B → C 各 span 的耗时。
    • 并行还是串行:几个下游调用如果是串行排列,优化空间立刻显现。
    • 慢 span 下钻:某个 RPC 800ms,点进去看它的子 span——是 DB 慢、Redis 慢还是又调了别人。
  3. 定位到具体代码:慢 span 上有实例、接口、方法名,去对应服务 Arthas trace 复现确认,或直接看该方法日志。

没有全链路追踪时的土办法

  • 各服务统一接入 MDC,日志打印 traceId(HTTP header / RPC attachment 透传),按 traceId 逐个服务 grep,拼时间线。
  • 网关与各服务 access log 的时间戳相减,二分定位最慢的一跳。

常见根因速查

  • 串行 RPC 链(A→B→C 串行,改成 CompletableFuture 并行,直接砍半 RT)。
  • 某一跳里藏着 N+1 查询或大对象序列化。
  • 重试叠加:上游超时重试导致下游排队。
  • GC 停顿:span 时间跨整个服务的所有子调用,且周期性出现。

我的习惯:上线前就把 SkyWalking 埋点覆盖 RPC/DB/Redis/MQ,慢请求告警自动附 trace 链接,故障时省 80% 沟通成本。