系统学习 故障排查学习路径:从方法论到系统、网络、数据库与 K8s

io-timeout

套接字操作超过截止时间返回 i/o timeout 的定位与修复

现象

客户端报 dial tcp <ip>:<port>: i/o timeout、read tcp <ip>:<port>: i/o timeout 或 write tcp <ip>:<port>: i/o timeout,Go 的 net.Error 会满足 Timeout() == true,DNS 解析也可能返回 lookup <name>: i/o timeout。它表示某次套接字操作在本地超时,不表示对端明确拒绝,也不等于对端没有响应。

要结合错误前缀判断阶段:dial 是建连超时,常见于 SYN 被丢弃;read 是连接已建立但在截止时间内没有读到数据;write 是发送缓冲区迟迟无法写入,通常对端读得太慢或窗口为零。不同阶段的根因和修复方向差别很大,不能只看到 i/o timeout 就调大超时。

快速定位

先用分阶段耗时确认卡在哪里:

curl -sS -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}\n' --connect-timeout 3 --max-time 10 https://<host>/
nc -vz -w 3 <host> <port>
ss -tin '( dport = :<port> or sport = :<port> )'
ss -lnt '( sport = :<port> )'
nstat -az TcpExtTCPTimeouts TcpExtTCPLostRetransmit TcpRetransSegs
mtr -n -T -P <port> -c 20 <host>
tcpdump -ni any -tttt 'host <host> and port <port>'
dig +time=3 +tries=1 <host>
journalctl -u <service> --since '30 min ago'
kubectl logs <pod> -n <ns> --previous

connect 有值而 first_byte 为 0,说明 TCP 已建连但服务端或中间层迟迟不回数据;connect 为 0 则连 SYN-ACK 都没回来。ss -tin 里持续增长的重传计数和较大的 Send-Q,分别指向链路丢包与应用没有及时读取。服务端同时看监听队列:Recv-Q 长期非零说明 accept 跟不上,Send-Q 堆积说明对端接收窗口或读取速度有问题。

如果只有大响应超时而小响应正常,优先查路径 MTU 和接收窗口;如果只有新建连接超时而已有连接正常,优先查 SYN 路径、监听队列和中间设备的新建连接限速。

Go 客户端应显式设置各阶段超时,避免默认无限等待:

client := &http.Client{
    Transport: &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   3 * time.Second,
            KeepAlive: 30 * time.Second,
        }).DialContext,
        TLSHandshakeTimeout:   5 * time.Second,
        ResponseHeaderTimeout: 10 * time.Second,
    },
    Timeout: 20 * time.Second,
}

常见根因

  1. 安全组、防火墙或网络 ACL 静默丢弃 SYN 或数据包,没有返回 RST,客户端只能等到超时。
  2. 路由不对称、VPN 或 NAT 会话失效,请求能出去但响应回不来。
  3. 链路丢包、重传次数耗尽,或 MTU 黑洞导致大包被静默丢弃。
  4. 服务端 accept 队列满、进程被限流或 CPU 饱和,SYN 和请求在队列里等待。
  5. 服务端读得慢:线程池耗尽、事件循环阻塞、磁盘或数据库卡住,接收窗口逐渐降为零。
  6. DNS 查询慢或上游不可达,解析阶段先耗尽总超时预算。
  7. 客户端超时设得过短,跨地域 RTT 和服务端处理时间叠加后必然超时。
  8. 连接池复用了已被负载均衡、NAT 或服务端关闭的空闲连接,第一次读写才发现连接已死。
  9. 未启用 TCP keepalive,路径中断后连接长期保持 established,直到应用层操作超时。
  10. 代理、WAF 或云负载均衡只完成前端连接,后端转发失败却不返回明确错误。

修复

相关命令

参考链接

读完自测

合上页面,先回答,再展开对照。

  1. i/o timeout 能说明请求一定没有到达服务端吗?
  2. curl 显示 connect 耗时为 0,最可能卡在哪一层,应先查什么?
  3. 连接池里的空闲连接为什么会在第一次读取时报 i/o timeout?
答案
  1. 不能。它只说明某次套接字操作在本地超时,包可能已经到达但响应丢失,也可能在拨号阶段就被丢弃;要结合错误前缀、抓包和服务端日志判断。
  2. connect 为 0 表示 TCP 三次握手没有完成。先查安全组和防火墙是否放行、路由与返回路径是否正常、服务端监听队列是否溢出,并用 tcpdump 看 SYN 与 SYN-ACK。
  3. 连接可能已被负载均衡、NAT 或服务端按空闲超时静默关闭,客户端仍把它当作可用连接复用;应让连接池空闲时间小于中间层超时,启用 keepalive 并在复用前校验连接。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令