← 中文资源

简体中文版本

诊断永远挂起的 API 请求

识别没有超时而卡死的请求,统计半开连接数,找到缺失截止时间的外部依赖,并补上端到端请求期限。

阅读英文原文

目标

挂起的服务很少烧资源:它把请求堆积在一个永远读不完的读操作上。证据不在 CPU 图里,而在套接字表里。统计指向单一依赖的 established 套接字数、再快照进行中请求的等待点,就能点名元凶。永久解法是给每个外部调用加截止时间,最好是从调用方预算推导并沿链路传播的同一份截止时间。

步骤要点

  1. 用 ss 统计目标依赖 443 端口的 established 套接字:CPU 空闲却有数百条连接,即是“读无期限”的典型signature。
  2. 向进程发诊断信号导出在途请求快照,确认全部停在等待 payments 响应的阶段。
  3. 用黑洞地址(10.255.255.1 只建连不应答)在预发确定性复现挂起,证明现状没有超时。
  4. 给调用加 AbortSignal.timeout(3000),复现场景改为三秒后抛 TimeoutError 中断。
  5. 把下游调用的 signal 换成从请求剩余预算推导的 ctx.deadline,让重试消耗同一预算而非叠加超时。

排错提示

加了超时池子仍被耗尽,是客户端对超时结果无上限重试,需按总耗时或次数封顶;超时触发但套接字残留说明 abort 未销毁连接,要确认客户端在 abort 时销毁 socket 并给连接池设空闲上限;只在生产出现就说明本地缺真实慢依赖,用黑洞地址在 CI 里断言截止时间行为。

适用边界

截止时间只是把等待换成错误,用户仍然失败,除非存在降级路径;单调用超时必要但不充分,不做预算传播时各阶段之和仍可能超出任何单一预算。

代码示例

# Requests parked on one dependency
ss -tn state established "( dport = :443 )" | grep -c <payment-ip>

# Reproduce a hang with a black-hole address (never responds)
PAYMENT_BASE_URL=http://10.255.255.1:9000 npm run reproduce:hang