Appearance
一次由 TCP TIME_WAIT 暴涨引发的故障排查
现象
压测高峰期,Nginx upstream 报错 connect() failed (99: Cannot assign requested address),页面偶发卡死。监控显示 Nginx 所在节点的 ss -s 中 TIME_WAIT 数量从平时的 2k 暴涨到 6w+。
环境
- OS: Ubuntu 22.04 LTS
- 内核: 5.15.0
- Nginx: 1.24.0
- 后端服务: Go 1.22 HTTP API,无连接池
排查会用到 ss 命令,如果系统提示未安装,可按发行版安装:
sh
# 安装 ss 命令所在的 iproute2 包
sudo apt-get update && sudo apt-get install -y iproute2sh
# 安装 ss 命令所在的 iproute 包
sudo yum install -y iproute排查过程
先看当前连接状态分布:
bash
ss -s输出:
text
Total: 60012 (kernel 60018)
TCP: 60005 (estab 12, closed 59993, orphaned 0, synrecv 0, timewait 59993/0)timewait 接近 6w,明显是本地端口段被占满。查看当前端口范围:
bash
sysctl net.ipv4.ip_local_port_range输出:
text
net.ipv4.ip_local_port_range = 32768 60999可用端口约 28k,但 TIME_WAIT 已经达到 6w,说明大量连接在极短时间内创建并关闭。
检查 Nginx 配置,发现 upstream 使用的是 HTTP/1.0 短连接:
nginx
upstream backend {
server 127.0.0.1:8080;
}
server {
listen 80;
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.0;
}
}proxy_http_version 1.0 且没有 Connection 相关配置,默认每次请求都新建 TCP 连接。
根因
Nginx 与后端服务之间使用 HTTP/1.0 短连接,每个请求完成后由客户端(Nginx)主动关闭连接,产生大量 TIME_WAIT。压测 QPS 高时,TIME_WAIT 数量超过本地可用临时端口范围,导致 Cannot assign requested address。
修复
将 Nginx upstream 改为 HTTP/1.1 长连接,并设置合理的 keepalive 连接池:
nginx
upstream backend {
server 127.0.0.1:8080;
keepalive 64;
}
server {
listen 80;
location /api/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}TIP
proxy_set_header Connection "" 必须加,否则 Nginx 会默认把客户端的 Connection: close 转发给后端,导致长连接失效。
同时,在 /etc/sysctl.conf 中开启 TIME_WAIT 快速回收(仅在内网、无 NAT 场景建议开启):
bash
echo 'net.ipv4.tcp_tw_reuse = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p修改后压测,ss -s 中 TIME_WAIT 稳定在 1k 以下,错误消失。
复盘
- 新增监控:
node_exporter采集node_netstat_TcpExt_TCPTimeWaitOverflow和ss -s中 TIME_WAIT 数量。 - 所有 Nginx upstream 配置统一审查,强制使用 HTTP/1.1 + keepalive。
- 压测方案补充连接状态指标,不再只看 QPS 和错误率。