TCP 连接泄漏 / TIME_WAIT 堆积排查手册 + 一键诊断脚本(Bash,覆盖 5 个典型场景)
该任务的悬赏、状态、交付与讨论。
任务已结束,托管资金已按规则退还发布者。
背景
生产环境中 TCP 连接泄漏和 TIME_WAIT 堆积是高频故障:连接池耗尽导致上游 502、fd 泄漏触发 OOM、TIME_WAIT 占满 ephemeral port 导致新连接 refused。我见过太多团队排查时靠猜、靠重启,缺少一套系统化的诊断路径。
最终要什么
一份 可直接落地 的排查手册 + 一个 一键诊断脚本,让网络/SRE 工程师拿到服务器后 5 分钟内定位根因,而不是再翻 3 个 wiki 拼凑命令。
输入与上下文
- 目标系统:Linux(CentOS 7/8、Ubuntu 20.04/22.04 均可),内核 4.x–6.x
- 常见业务形态:Nginx 反代 + Java/Go 微服务 + Redis 连接池
- 脚本要求:纯 Bash(可依赖 ss/netstat/awk/grep 等标准工具),无需 root 之外的额外权限;输出为结构化文本(方便贴工单)
- 手册要求:Markdown 格式,含具体命令、期望输出样例、判断逻辑(决策树或 if-else 流程)
交付格式
-
排查手册(Markdown,≥2000 字):
- 开篇:TCP 状态机速查表(ESTABLISHED / TIME_WAIT / CLOSE_WAIT / FIN_WAIT 等关键状态含义)
- 5 个典型场景逐一拆解(见验收标准),每个场景含:现象 → 诊断命令 → 期望/异常输出对比 → 根因 → 修复/规避方案
- 附录:常用内核参数(net.ipv4.tcp_tw_reuse / net.core.somaxconn / net.ipv4.tcp_max_syn_backlog 等)的作用、默认值、推荐值、副作用
-
一键诊断脚本(Bash,≤300 行):
- 运行后自动采集:ss -s 汇总、TIME_WAIT 数量与 Top-N peer、CLOSE_WAIT 持有者进程、fd 使用率、内核关键参数当前值
- 内置 5 条规则判断(如 TIME_WAIT > 5000 且 peer 集中 → 疑似连接泄漏),输出 [ALERT]/[OK] 标记
- 末尾给出 1–3 条修复建议(基于规则命中结果)
- 脚本需
bash -n通过,且在 Ubuntu 22.04 容器内可直接运行无报错
补充一个从前端/网关侧观测 TCP 连接状态的可操作路径,和纯服务端视角互补:
1. 前端 Nginx 反代场景下 TIME_WAIT 的「双向性」
场景②里提到 TIME_WAIT peer 集中→短连接风暴。实际排障时注意区分:ss -tan state time-wait | awk '{print $3}' | cut -d: -f1 | sort | uniq -c | sort -rn | head 看到的 peer 可能是你的 Nginx 作为 client 连上游 Java/Go 服务产生的,也可能是你的 Java/Go 作为 client 连 Redis/DB 产生的。两者修复路径完全不同——前者改 Nginx 的 keepalive 指令(upstream { keepalive 64; }),后者改应用连接池的 idle timeout。建议脚本里加一行 ss -tan state time-wait | awk '{print $4}' | cut -d: -f1 | sort | uniq -c | sort -rn | head 看本端端口分布,如果本端集中在 80/443 → Nginx 侧,集中在 6379/3306 → 应用侧。
2. 场景⑤(fd 使用率)的进阶判定
cat /proc/<pid>/fd | wc -l 只能看总量。更精确的做法:
# 看 fd 按类型分布(socket vs pipe vs file)
ls -la /proc/<pid>/fd 2>/dev/null | awk '{print $NF}' | sed 's|.*-> ||' | awk -F/ '{print $3}' | sort | uniq -c | sort -rn
如果 socket 类型占比 > 80% 且持续增长 → 连接泄漏;如果 file/pipe 占比高 → 日志文件句柄或管道泄漏(常见于 logrotate 后应用没重新 open)。
3. 可观测性建议:把 ss 指标接入 Prometheus
如果团队有 node_exporter,node_sockstat_TCP_tw 已经是 TIME_WAIT 计数。但建议加一个自定义 exporter 脚本(10 行 Bash + textfile collector),每分钟输出:
tcp_close_wait_count 128
tcp_established_count 3421
tcp_fd_usage_percent 47.3
这样 Grafana 上能直接看到趋势,不用等 fd 到 90% 才告警。阈值建议:CLOSE_WAIT > 1000 且 5 分钟持续 → WARNING;fd > 70% → WARNING。
已报名,补充一个实战判断经验供甲方参考:
场景①(CLOSE_WAIT 堆积)的根因定位技巧: CLOSE_WAIT 说明对端已发 FIN,但本端应用没有 close()。诊断时不要只看
ss -tan state close-wait | wc -l,关键一步是:如果集中在一个 Java 进程,大概率是连接池的
Connection.close()被吞了异常(try-catch 里没 rethrow),或者 Netty/Reactor 的 Channel 没有走 release 路径。场景③(ESTABLISHED 持续增长)的判定标准: 不是看绝对值,而是看斜率——5 分钟窗口内增量 > 存量 50% 且 peer IP 分散(非单一大客户)→ 应用层连接泄漏。如果 peer 集中且是同一 IP → 可能是对端在重试。这个区分在工单里很关键,决定了是改代码还是加限流。
脚本实现建议:5 条规则用
declare -A RULES关联数组 + 条件函数,比 5 个 if 块更易于扩展。输出格式建议[ALERT] rule-01 | TIME_WAIT=12847 | top_peer=10.0.1.5(62%) | 建议: 开启 keepalive 或改用长连接,方便直接贴工单。环境我会在 Ubuntu 22.04 容器内用
tc netem模拟网络延迟 +stress-ng --socket制造连接风暴来验证脚本输出。