
Linux 服务器上的 CLOSE_WAIT 持续增加,通常说明对端已经关闭连接,本地应用也收到了关闭通知,但应用程序迟迟没有关闭对应的 socket。少量、短暂的 CLOSE_WAIT 属于正常现象;如果数量只增不减,并伴随 too many open files、新连接失败或接口超时,就要按连接泄漏处理。
这类问题多半不能靠修改 TCP 内核参数解决。排查重点应放在占用连接的进程、文件描述符数量、异常处理路径、连接池以及反向代理与上游服务的连接生命周期。
CLOSE_WAIT 表示什么
TCP 连接关闭时,对端发送 FIN,本地内核确认后,连接会进入 CLOSE_WAIT。随后要由本地应用调用 close(),内核才能继续发送 FIN,完成后续关闭过程。
因此,CLOSE_WAIT 直接反映的是本地应用尚未完成关闭动作。常见原因包括:异常分支漏掉关闭逻辑、请求超时后连接没有归还、线程阻塞、事件循环卡住、连接池状态异常,或某个 SDK、数据库驱动存在连接释放问题。
CLOSE_WAIT 与另外两种常见状态不要混淆:
TIME_WAIT多出现在主动关闭连接的一方,用于避免旧报文影响后续连接。FIN_WAIT_2表示本地主动关闭后,仍在等待对端发送 FIN。CLOSE_WAIT表示对端已经提出关闭,本地应用还没有关闭 socket。
先确认 CLOSE_WAIT 是否持续堆积
先看 TCP 总体状态:
ss -s
列出全部 CLOSE_WAIT 连接:
ss -tan state close-wait
只统计数量:
ss -Htan state close-wait | wc -l
连续观察数量变化:
watch -n 2 'ss -Htan state close-wait | wc -l'
一次统计值不能说明连接泄漏。更值得警惕的是数量连续上升,业务低峰期仍不回落,或总是集中在同一个本地端口、远端地址和进程。
按端口和远端地址缩小范围
下面的命令按本地地址与端口统计连接数量:
ss -Htan state close-wait | awk '{print $4}' | sort | uniq -c | sort -nr | head按远端地址与端口统计:
ss -Htan state close-wait | awk '{print $5}' | sort | uniq -c | sort -nr | head不同发行版和 ss 版本的输出列可能略有差异,IPv6 地址也包含冒号。运行命令后应先查看几行原始输出,再确认 $4、$5 是否对应本地和远端地址,不要直接用冒号切割 IPv6 字段。
如果连接集中在 80、443、数据库端口或某个内部 API 端口,通常可以快速判断问题属于 Web 服务、反向代理、数据库客户端还是业务程序。
找出占用连接的进程
有 root 权限时,可以让 ss 显示进程信息:
sudo ss -tanp state close-wait
也可以使用 lsof:
sudo lsof -nP -iTCP -sTCP:CLOSE_WAIT
重点记录进程名、PID、本地端口和远端地址。若大量连接归属于同一个 PID,后续检查就应围绕该进程展开。容器环境还要确认 PID 属于哪个容器或 Pod,避免只在宿主机上看到代理进程,却漏掉实际业务实例。
检查文件描述符是否接近上限
每个未关闭的 socket 都会占用文件描述符。确定 PID 后,可以查看进程当前打开的描述符数量:
pid=<PID> ls -1 /proc/$pid/fd | wc -l
查看该进程的软限制和硬限制:
cat /proc/$pid/limits | grep -i 'open files'
查看系统已分配的文件句柄情况:
cat /proc/sys/fs/file-nr sysctl fs.file-max
如果进程 FD 数量不断增长并接近 Max open files,日志中可能出现 Too many open files、连接建立失败或无法打开普通文件。单纯提高 ulimit -n 只能延后故障出现,连接没有被正确释放时,新的上限最终也会被耗尽。
应用程序中最常见的泄漏位置
异常分支没有关闭连接
正常请求路径调用了 close(),但超时、解析失败、提前返回或抛出异常时没有执行关闭逻辑。检查代码时,应覆盖所有返回路径,并使用语言提供的自动清理机制,例如 finally、defer、with 或资源托管对象。
HTTP 或数据库连接池没有归还连接
连接池不代表连接一定会被安全回收。响应体未完整读取、结果集未关闭、事务未结束或借出的连接没有归还,都可能让 socket 长时间留在进程中。需要同时检查连接池的活动连接数、空闲连接数、等待队列和回收日志。
超时只中断业务逻辑,没有释放底层 socket
有些代码在业务层返回“请求超时”,底层网络调用仍然阻塞。客户端连接超时、读取超时、写入超时和连接池等待超时应分别设置,并确认超时触发后会取消请求、关闭响应对象或归还连接。
线程、协程或事件循环卡住
如果负责清理连接的线程无法继续运行,关闭动作会一直延迟。Java 服务可结合线程转储,Go 服务可查看 goroutine,Node.js 服务可检查事件循环阻塞和未结束的请求。此时只看 TCP 状态不够,还要把连接增长时间与线程栈、GC、CPU 和接口延迟对齐。
反向代理与上游连接生命周期不一致
NGINX、应用网关或服务网格可能与客户端、上游服务分别维护连接。客户端连接已经关闭,不代表代理到上游的连接也按预期释放。检查 keepalive、读写超时、上游健康状态和应用日志时,要区分连接究竟停留在哪一段。
业务正在受影响时怎么处理
如果 FD 即将耗尽,应先保存现场信息,再做最小影响的恢复操作。建议至少保留以下内容:
date ss -s ss -tanp state close-wait ls -1 /proc/<PID>/fd | wc -l cat /proc/<PID>/limits
同时保存应用错误日志、线程或协程状态,以及出问题时间段的请求量和延迟。完成取证后,可以逐实例摘流、滚动重启或灰度重启故障进程,让连接暂时释放。直接同时重启全部实例容易造成业务中断,也会丢失定位问题所需的现场。
重启只能恢复容量,不能证明问题已经修复。如果应用重新启动后 CLOSE_WAIT 又以相同速度增长,泄漏路径仍然存在。
哪些操作通常解决不了 CLOSE_WAIT
修改 net.ipv4.tcp_fin_timeout
tcp_fin_timeout 主要控制孤儿连接在 FIN-WAIT-2 状态保留的时间。CLOSE_WAIT 正在等待本地应用关闭 socket,缩短这个参数不会替应用执行 close()。
一味提高文件描述符上限
提高上限可以在确认业务确实需要更多并发连接时使用,但应同时完成容量评估。存在连接泄漏时,提高上限只会让进程积累更多未关闭连接,并增加最终故障的影响范围。
强行清理所有连接
批量终止连接可能影响正常请求,而且无法修复应用的资源释放逻辑。线上需要应急时,应先定位进程和实例,再采用摘流、滚动恢复等可控方式。
修复后如何验证
修复完成后,至少观察一个完整的业务高峰和低峰周期:
watch -n 5 'ss -Htan state close-wait | wc -l'
同时检查以下指标:
CLOSE_WAIT数量是否在合理范围内波动,而不是持续单向增长。进程 FD 数量是否能够回落,并与并发请求量大致同步。
新连接成功率、接口错误率和延迟是否恢复正常。
连接池等待数、活动连接数和空闲连接数是否符合配置。
重启后经过同等流量周期,问题是否再次出现。
最好为 CLOSE_WAIT 数量、进程 FD 使用率和连接池等待队列设置趋势告警。固定阈值可作为兜底,增长速度往往能更早暴露泄漏。
常见问题
CLOSE_WAIT 数量多少算异常?
没有适用于所有服务器的固定数字。短连接较多的业务可能暂时出现一批 CLOSE_WAIT。判断时应结合持续时间、增长趋势、FD 使用率和业务错误。长期不回落且集中在同一进程,比某个瞬时数量更有诊断价值。
CLOSE_WAIT 会自动消失吗?
只有本地应用关闭 socket,或进程退出使内核回收资源后,相关连接才会结束。依赖系统长时间等待并不能替代应用修复。
重启 NGINX 能解决吗?
如果连接属于 NGINX,重启或平滑重载可能暂时释放资源;如果连接属于上游应用,重启 NGINX 未必有效。先用 ss -p 或 lsof 确认 PID,再决定处理哪个服务。
CLOSE_WAIT 和端口耗尽是一回事吗?
两者不是同一个概念。CLOSE_WAIT 连接会占用 socket 和文件描述符,大量堆积可能间接导致资源不足;客户端主动建立大量出站连接时,还可能伴随临时端口压力。排查时要分别查看 FD、连接状态和本地端口使用情况。
结论
服务器出现大量 CLOSE_WAIT 时,排查顺序可以固定下来:确认数量和趋势,按地址与端口聚合,定位 PID,检查文件描述符,再回到应用的异常路径、连接池、超时和线程状态。恢复业务时可以滚动重启故障实例,但最终修复应落在应用正确关闭 socket,并通过连接趋势和 FD 变化验证结果。
参考资料
RFC Editor:Transmission Control Protocol (TCP), RFC 9293
https://www.rfc-editor.org/rfc/rfc9293Linux man-pages:ss(8)
https://man7.org/linux/man-pages/man8/ss.8.htmlLinux Kernel Documentation:IP Sysctl
https://docs.kernel.org/networking/ip-sysctl.htmlLinux Kernel Documentation:/proc 文件系统
https://docs.kernel.org/filesystems/proc.htmlNGINX 官方文档
https://nginx.org/en/docs/
资料核查日期:2026年8月31日。