CLOSE_WAIT 意味着什么
TCP 四次挥手时,如果对方先发 FIN,本端内核会自动回一个 ACK,连接随即进入 CLOSE_WAIT,接下来要等应用程序调用 close 才会发出自己的 FIN。也就是说,这个状态该停留多久完全由应用决定。因此 CLOSE_WAIT 数量只增不减,基本可以断定是应用层没有关闭连接,而不是内核参数或网络问题,调内核参数是南辕北辙。
定位泄漏进程
定位泄漏进程
ss -tan state close-wait | wc -l # 先看总量,持续增长才是问题
ss -tanp state close-wait | head -20 # 加 -p 看归属进程(需 root)
ss -tan state close-wait | awk '{print $5}' | sort | uniq -c | sort -rn | head # 按对端聚合,找出泄漏来源
ls /proc/<pid>/fd | wc -l # 对照该进程 fd 数量,逼近上限就危险
ulimit -n # 查看进程 fd 上限输出逐列读法
| 列 | 含义 | 怎么看 |
|---|---|---|
| State | 连接状态,如 CLOSE-WAIT | 数量平滑而不下降就是泄漏 |
| Recv-Q | 接收队列里还没被应用读走的字节 | 长期非 0 说明应用卡住没读数据 |
| Send-Q | 发送队列里还没被确认的字节 | 持续非 0 通常是对端不接收 |
| Local Address:Port | 本端地址与端口 | 据此判断是哪个服务在泄漏 |
| Peer Address:Port | 对端地址与端口 | 按对端聚合能定位是哪个上游引起的 |
| users:(...) | 归属进程与 pid | 需要 root 权限才显示,是定位根因的关键 |
常见根因与后果
| 代码分支遗漏 | 异常路径提前返回,连接没有走到关闭逻辑 |
|---|---|
| 连接池不回收 | 借出的连接没归还,或归还时未真正关闭 |
| 线程阻塞 | 线程卡在别处,finally 或 defer 迟迟未执行 |
| 后果 | fd 耗尽后新连接直接报 Too many open files,服务整体不可用 |
修复只能从应用侧入手:给读写设置超时、确保 defer 或 finally 关闭连接、检查连接池归还逻辑。内核参数再调也不会替你调用 close。
如果 CLOSE_WAIT 与大量 TIME_WAIT 同时出现,要分开看:CLOSE_WAIT 是应用程序的问题,TIME_WAIT 是主动关闭方的正常状态,两者处置方式完全不同。