← [ BACK_TO_NOTES ]

服务请求卡顿的分层排查

从影响范围、DNS、连接、TLS、HTTP/代理、应用调度、连接池、数据库锁与下游依赖逐层定位请求卡顿,并找到第一个异常等待边界。

#debug #evergreen #backend #network

[!info] related notes

服务请求卡顿的分层排查

现象先量化

“接口卡住”至少可能表示:

  • DNS / connect 很慢;
  • TTFB 很慢;
  • headers 已到但 body / stream 不再推进;
  • 客户端渲染卡;
  • 服务端请求仍在排队;
  • DB lock 或下游 timeout;
  • 请求已在服务端完成,但响应路径断开。

先记录:开始时间、endpoint、用户/区域、requestId/runId、客户端 timeout、是否可复现、最近 deploy/config 变化。

第一步:确定范围

  • 单用户还是全部用户;
  • 单区域 / 网络还是所有区域;
  • 单 endpoint 还是整个服务;
  • 读请求还是写请求;
  • 突发还是渐进变慢;
  • p50、p95、p99 哪一段恶化。

范围能快速区分客户端、网络、实例、依赖和数据热点。

分层路径

Client / DNS
→ TCP / QUIC / TLS
→ CDN / LB / Reverse Proxy / HTTP
→ Application Queue / Event Loop / Thread Pool
→ Connection Pool
→ Database Lock / Query Plan
→ Downstream Service / Model Provider
→ Response Streaming / Client Rendering

DNS

观察 dig / nslookup、浏览器 timing、缓存命中和不同 resolver / 网络对比。DNS 正常只证明拿到地址,不证明端口可连接。

Connect / TLS

分别测:

  • TCP connect;
  • TLS handshake;
  • 证书 / SNI;
  • packet loss / retransmission;
  • IPv4 / IPv6;
  • 连接复用是否失效。

不要把 connect timeout 误判为应用 500。

HTTP / Proxy

检查:

  • LB target 健康;
  • proxy upstream timing;
  • request/body size;
  • buffering;
  • timeout 配置;
  • rate limit;
  • connection / stream limit;
  • retry 是否放大流量。

SSE/streaming 要区分 TTFB、首 event、event gap 和终态。

应用调度

关注:

  • CPU / memory / GC;
  • event-loop lag;
  • thread / worker pool saturation;
  • queue depth;
  • goroutine/task 堆积;
  • 锁竞争;
  • 大 JSON、同步 I/O、阻塞 SDK;
  • deadline 是否向下游传播。

Connection Pool

请求可能不是 SQL 慢,而是在等待连接。观察 active/idle/wait count、acquire latency、leak、transaction duration 和 pool timeout。

Database

  • slow query / EXPLAIN ANALYZE;
  • lock wait / deadlock;
  • rows estimate 与真实 rows;
  • seq scan / join / sort spill;
  • vacuum / bloat / statistics;
  • hot row / long transaction;
  • IOPS / CPU / buffer cache。

Downstream

为每个 HTTP/RPC/model/tool call 记录独立 span、timeout、retry count 和 status。没有分段耗时,所有时间都只会显示在上游总 latency 中。

客户端

Response 已快速到达但页面仍“卡住”时,检查主线程 long task、流式 parser、逐 token render、大 DOM、layout、syntax highlight 和错误状态未收敛。

找第一个错误边界

不要问“哪个组件报错最大”,而要找:

正常信号最后出现在哪里?从哪一层开始等待时间异常、状态不推进或 contract 被破坏?

Trace 最适合回答跨服务耗时;没有 trace 时,在边界处增加 timestamp 和 correlation id,做一次受控复现。

超时与最终状态

客户端超时不代表服务端停止执行。对创建任务、支付、Agent 工具等有副作用操作,超时后应通过 idempotency key / operation id 查询最终状态,不能盲目重试。

快速恢复与根因修复

恢复动作可以是:

  • 限流 / shedding;
  • 停止重试;
  • 熔断故障下游;
  • 回滚最近变更;
  • feature flag 降级;
  • 扩容明确饱和资源;
  • 终止异常长事务。

恢复后仍要补:根因、触发条件、监控缺口、回归测试、容量或 timeout policy,不能把“重启后好了”当结论。

面试回答

我会先确认影响范围和慢在 connect、TTFB、stream 还是前端渲染,再沿 DNS、TCP/TLS、HTTP/代理、应用队列或 Event Loop、连接池、数据库锁/慢 SQL和下游依赖逐层看分段耗时。优先用 metrics 找范围、trace 找跨服务等待、logs 看具体错误;目标是定位第一个异常边界。还要注意客户端超时不代表服务端停止,有副作用请求必须用 operation id 查询最终状态,避免重试造成重复执行。

迁移检查

给一个新的“API 偶发 8 秒卡顿”案例,不看正文回答:

  1. 先要把“慢”拆成哪些可观测阶段(DNS/connect/TLS/TTFB/stream/client render 等)?
  2. 为什么第一目标是找到“第一个异常等待边界”,而不是直接找 CPU 最高的组件?
  3. metrics、trace、logs 分别最适合回答什么问题?
  4. 连接池耗尽与数据库慢 SQL可能怎样在上游表现成相似延迟?
  5. 客户端超时后为什么不能假设服务端已经停止副作用操作?
  6. 对支付/创建任务类请求,超时后的正确动作为什么通常是按 operation/idempotency key 查询最终状态,而不是盲重试?
  7. 临时扩容或重启恢复以后,还必须补哪些根因与预防证据?
创建于 2026/9/2 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本