服务请求卡顿的分层排查
从影响范围、DNS、连接、TLS、HTTP/代理、应用调度、连接池、数据库锁与下游依赖逐层定位请求卡顿,并找到第一个异常等待边界。
[!info] related notes
- 页面链路:输入 URL 到页面展示
- AI 首字延迟:AI 助手首字延迟优化
- 追踪:多服务分布式追踪
- 数据库:数据库索引 · 数据库锁
服务请求卡顿的分层排查
现象先量化
“接口卡住”至少可能表示:
- 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 秒卡顿”案例,不看正文回答:
- 先要把“慢”拆成哪些可观测阶段(DNS/connect/TLS/TTFB/stream/client render 等)?
- 为什么第一目标是找到“第一个异常等待边界”,而不是直接找 CPU 最高的组件?
- metrics、trace、logs 分别最适合回答什么问题?
- 连接池耗尽与数据库慢 SQL可能怎样在上游表现成相似延迟?
- 客户端超时后为什么不能假设服务端已经停止副作用操作?
- 对支付/创建任务类请求,超时后的正确动作为什么通常是按 operation/idempotency key 查询最终状态,而不是盲重试?
- 临时扩容或重启恢复以后,还必须补哪些根因与预防证据?