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

tls-handshake-timeout

TCP 已建立但在规定时间内未完成 TLS 握手,客户端报 TLS handshake timeout 的定位与修复

现象

Go 客户端报 net/http: TLS handshake timeout,其他客户端可能显示 SSL_connect timeout、handshake timed out 或连接在 HTTPS 请求开始后长时间无响应。它发生在 TCP 连接已经建立之后、TLS 握手完成之前,通常由客户端的 TLS 握手超时触发,而不是 DNS 或 TCP 拨号超时。

要和 tls-handshake-failed 区分:握手失败往往很快返回 alert,例如证书、协议版本或加密套件不匹配;握手超时则是握手消息没有在截止时间内交换完,常见于丢包、延迟过大、MTU 黑洞、服务端过载或中间设备只转发了部分字节。TCP 能连上不代表 TLS 一定能完成,这正是排查的关键。

快速定位

先确认卡在 TCP 还是 TLS 阶段:

curl -v --connect-timeout 3 --max-time 15 -o /dev/null \
  -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} total=%{time_total}\n' \
  https://<host>/
openssl s_client -connect <host>:443 -servername <host> -state -brief </dev/null
openssl s_client -connect <host>:443 -servername <host> -tls1_2 -state </dev/null
ss -lnt '( sport = :443 )'
ss -tin '( dport = :443 or sport = :443 )'
mtr -n -T -P 443 -c 20 <host>
sudo ping -M do -s 1472 <host>
tcpdump -ni any -tttt 'host <host> and port 443'
journalctl -u <service> --since '30 min ago'
kubectl logs <ingress-pod> -n <ns> --tail=100

connect 有值但 tls 为 0,说明卡在握手;openssl s_client -state 会显示最后完成的握手步骤,能看出是等待 ServerHello、证书还是密钥交换。抓包重点看 ClientHello 是否发出、ServerHello 是否返回、证书包是否因过大而丢失,以及是否有大量重传。

服务端检查 TLS 终止进程的 CPU、连接数和证书加载方式:

top -H -p $(pgrep -f <tls-service> | head -1)
ss -s
openssl x509 -in /path/to/fullchain.pem -noout -subject -issuer -dates

Go 客户端应把拨号、TLS 握手和响应头超时分开配置,避免一个总超时掩盖具体阶段:

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

常见根因

  1. 链路丢包或 RTT 过大,握手消息和证书需要多次重传,超过客户端握手超时。
  2. MTU 黑洞:ServerHello 或证书链超过路径 MTU,ICMP 分片需求被丢弃,握手卡住。
  3. 服务端 TLS 终止进程 CPU 饱和、线程或事件循环阻塞,握手排队等待。
  4. accept 队列或 TLS 握手队列溢出,TCP 已建立但应用迟迟不处理。
  5. 防火墙或负载均衡只放行 TCP,未正确转发后续字节,或半连接代理未完成后端连接。
  6. 客户端通过 HTTPS_PROXY 访问,代理的 CONNECT 建立后卡住或超时。
  7. 证书链过长、动态查询证书慢或 OCSP 获取阻塞,握手响应被拖慢。
  8. 客户端请求了服务端不支持的协议或需要额外往返的扩展,叠加高延迟后超时。
  9. 证书私钥解密慢、硬件加速不可用或连接速率限制触发。
  10. 客户端握手超时设得过短,跨地域访问时正常握手也来不及完成。
  11. 服务端或中间设备对新建连接限速,SYN 通过了但握手消息被丢弃。
  12. 连接被调度到异常后端,健康检查只测 TCP 端口而没有完成 TLS 握手。

修复

相关命令

参考链接

读完自测

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

  1. TLS handshake timeout 和 tls: handshake failure 的含义有什么不同?
  2. TCP 能连通、普通小请求也正常,但 HTTPS 握手总是卡住,应优先怀疑什么?
  3. curl 显示 connect 正常而 tls 为 0,下一步应查哪些点?
答案
  1. 握手超时表示握手消息没有在截止时间内完成,常见于丢包、延迟、MTU 或服务端过载;握手失败通常会快速返回协议 alert,多与证书、协议版本或加密套件有关。
  2. 优先怀疑 MTU 黑洞:证书链或 ServerHello 超过路径 MTU,分片需求又被防火墙丢弃;用 ping -M do 逐级探测,配合抓包看大包是否丢失并检查 MSS clamping。
  3. 用 openssl s_client -state 看最后完成的握手步骤,抓包确认 ClientHello 与 ServerHello 是否往返,并检查服务端 TLS 进程 CPU、握手队列、证书加载、代理 CONNECT 与防火墙转发。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令