Linux服务器出现大量CLOSE_WAIT怎么办?连接泄漏、文件描述符耗尽与应用程序排查教程
本内容发表于:2026-08-31 14:16:19
浏览量
1054

Linux服务器CLOSE_WAIT连接、文件描述符与应用排查示意图

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 变化验证结果。

参考资料

  1. RFC Editor:Transmission Control Protocol (TCP), RFC 9293
    https://www.rfc-editor.org/rfc/rfc9293

  2. Linux man-pages:ss(8)
    https://man7.org/linux/man-pages/man8/ss.8.html

  3. Linux Kernel Documentation:IP Sysctl
    https://docs.kernel.org/networking/ip-sysctl.html

  4. Linux Kernel Documentation:/proc 文件系统
    https://docs.kernel.org/filesystems/proc.html

  5. NGINX 官方文档
    https://nginx.org/en/docs/

资料核查日期:2026年8月31日。