linux服务器出现大量CLOSE_WAIT状态的连接

本文探讨了Node.js服务因大量CLOSE_WAIT状态连接导致内存和CPU占用率飙升的问题,并详细分析了问题产生的原因,包括客户端频繁重连、服务器处理不当等。最终通过调整Redis持久化策略和限制Node.js进程数量解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.kankanan.com/article/linux-670d52a1566851fa73b0592791cf-close_wait-72b6600176848fde63a5.html

昨天服务器停止服务,node.js进程耗光了服务器的内存及CPU,node.js进程卡死无法被 kill 掉,最后要来root帐号密码,直接 kill -9 才结束掉进程。

再次鄙视一下 forever ,杀不掉原来的 node.js 进程组也就罢了,竟然又拉起了一套新的 node.js 进程组。

统计了一下 10 万个fd都耗光了,其中 9 万多个为 CLOSE_WAIT 状态,此时服务器已经无法响应请求。

CLOSE_WAIT 状态介绍

先看一副TCP连接关闭的状态图( 来源 ):

tcp_normal_close.png

被动关闭一方才会出现 CLOSE_WAIT 状态,由于被动关闭方未调用 close 关闭socket导致,问题肯定是由服务器代码引起。

检测到对端socket关闭然后关闭本端socket是由 node.js 自行完成的,最大的可能是没有机会执行 close 。

我们的应用客户端与服务器有一个tls长连接,当连接断开时客户端会等待3-10秒后尝试重连服务器,如果服务器出现卡顿会导致客户端频繁重连,

如果服务器来不及关闭这些连接,则会出现 CLOSE_WAIT 状态的连接,占用大量文件描述符,减少 CLOSE_WAIT 超时时间能够在一定程度上缓解这个问题,

但是对于我们这种长连接的环境,大量CLOSE_WAIT是问题的表象,而非根源。

参考:《解决CLOSE_WAIT 问题

内存及CPU占用彪升问题

伴随着 CLOSE_WAIT 出现的状况是 node.js 进程内存及CPU占用超高,单node.js进程内存占用达到 1.5G,CPU占用 90% 以上,此时应该会导致 node.js 响应慢, 来不及关闭连上来的socket。

所以解决问题的关键就是:找出什么原因导致 node.js 内存及CPU 100%占用。

想到的可能是redis负载过高引起,从运维监控图上可以看出一些蹊跷,node.js出问题时redis的连接数也同样彪升,而出问题的机器上刚好就是跑redis的机器, 另一台服务器一直相安无事,没有跑redis。

一次午夜故障元凶浮出水面

在晚上两点的时候服务出现问题,同样的现象,特别留意了一下redis的统计,请求速度很低,只有1200,平时都是5000。偶然在进程列表中发现了 redis-rdb-bgsave 的身影, 不断地执行ps看进程列表,发现 redis-rdb-bgsave 进程不断地出现,查看redis的持久化配置如下:

save 900 1
save 300 10
save 60 10000

我们的系统有大量的redis,1分钟肯定过万,这样redis持久化变是常态了,而且由于用的是机械硬盘,持久化肯定会引起系统卡顿,先将它调整为15分钟最多持久化一次:

config set save "900 1"

重启程序释放资源后系统开始正常响应,但是10多分钟后系统再次无响应,才想起一则经验教训:

     跑redis的机器至少要预留和redis占用内存同样大小的空闲内存空间,redis RDB持久化进行fork时最坏会占用双倍内存,内存不足就会动用交换分区,系统性能急剧下降。

于是,立即改配置将redis所在机器上的node.js cluster进程数调小,腾出大把内存,总算没有再出现问题,今晚终于可以入眠。

更多疑问

  • 我们的node.js进程为什么常常会占用很多内存?
  • netstat中看到CLOSE_WAIT状态的连接输入缓冲往往有数据,而ESTABLISHED状态的连接读写缓冲区往往为空,为什么?
  • node.js卡顿时forever杀不死反而启动了新实例帮倒忙,pm2就一定能够解决吗?
  • redis持久化引起服务挂掉,已经是在第二个项目中遇到了,终极解决方案是什么?

<think>好的,我需要解决用户关于Linux服务器上出现大量CLOSE_WAIT状态的问题。首先,我得回顾一下TCP连接状态转换过程,尤其是CLOSE_WAIT和TIME_WAIT的区别。根据引用[5],CLOSE_WAIT出现在被动关闭连接的一方,也就是服务器在接收到客户端的FIN报文后进入这个状态,等待应用程序关闭连接。而TIME_WAIT则是主动关闭连接的一方在发送最后一个ACK后进入的状态,用来处理可能延迟的报文。 用户的问题是服务器上有大量CLOSE_WAIT,这说明服务器端的应用程序没有正确关闭连接。引用[4]提到,检测到对端关闭后,node.js会自动关闭本端socket,但如果应用程序没有执行close操作,就会导致CLOSE_WAIT堆积。因此,根本原因可能在于代码中没有正确释放连接资源。 接下来,我需要整理解决方案的步骤。首先,应该检查应用程序代码,确保所有socket连接在使用后正确关闭,特别是数据库连接、文件句柄等资源。引用[3]指出服务端使用短连接时,主动关闭连接会导致TIME_WAIT,但这里的问题不同,是CLOSE_WAIT,所以重点在代码层面的资源释放。 然后,查看系统参数,虽然引用[2]提到TIME_WAIT可以通过优化服务器参数解决,但CLOSE_WAIT更多是应用程序的问题,系统参数调整可能有限,但可以检查文件描述符限制,比如ulimit -n的设置,避免资源耗尽。 接下来,网络排查。使用netstat或ss命令查看具体连接情况,确认是哪些进程导致的CLOSE_WAIT,结合lsof或/proc文件系统定位到具体进程和代码位置。例如,使用netstat -ae | grep CLOSE_WAIT,类似引用[1]中的命令,但替换为CLOSE_WAIT状态。 另外,引用[3]提到服务端主动关闭连接导致TIME_WAIT,但这里的情况是服务端被动关闭,所以需要确保应用程序在收到FIN后及时close。如果是HTTP服务,检查是否使用了Keep-Alive,适当调整超时时间,避免连接长时间未关闭。 可能还需要使用压力测试工具模拟高并发场景,观察CLOSE_WAIT的增长情况,验证修复措施是否有效。同时,监控工具如Prometheus或Grafana可以帮助实时监控连接状态变化。 最后,总结解决方案步骤,从代码检查、资源释放、系统参数、网络排查到监控优化,逐步排除问题。需要提醒用户,CLOSE_WAIT问题通常由应用程序未正确关闭连接引起,与TIME_WAIT不同,后者更多是系统参数调整的问题,如引用[2]和[3]所述。 需要确保回答结构清晰,分步骤说明,并引用相关参考资料。同时,生成的相关问题要围绕TCP状态、区别、其他状态的解决方案等。</think>### 处理Linux服务器大量CLOSE_WAIT连接的解决方案 #### 1. **理解CLOSE_WAIT状态的本质** CLOSE_WAITTCP连接关闭过程中的一个中间状态,表示**被动关闭方(通常是服务器)已收到主动关闭方(客户端)的FIN报文**,但尚未关闭本端连接[^5]。大量CLOSE_WAIT连接的堆积表明**应用程序未正确关闭socket连接**,导致资源无法释放[^4]。 --- #### 2. **排查与解决步骤** ##### **步骤1:检查应用程序代码** - **核心问题**:未显式调用`close()`方法释放连接- **常见场景**: - 数据库连接未关闭 - HTTP请求未关闭响应流 - 文件句柄泄漏 - **代码修复示例**(以Python为例): ```python # 错误示例:未关闭socket def handle_request(client_socket): data = client_socket.recv(1024) # 处理请求但未关闭连接 # 正确示例:显式关闭连接 def handle_request(client_socket): try: data = client_socket.recv(1024) # 处理请求 finally: client_socket.close() # 确保关闭 ``` ##### **步骤2:检查系统资源限制** - 使用命令查看当前进程打开的文件描述符数量: ```bash lsof -p <进程PID> | wc -l ``` - 调整系统文件描述符限制(临时生效): ```bash ulimit -n 65535 # 修改为更高限制 ``` ##### **步骤3:网络连接分析** - 使用`netstat`或`ss`命令定位问题: ```bash netstat -antop | grep CLOSE_WAIT # 查看所有CLOSE_WAIT连接 ss -s | grep CLOSE_WAIT # 更高效的统计方式 ``` - 结合`lsof`追踪具体进程: ```bash lsof -i :<端口号> # 查看占用端口的进程 ``` ##### **步骤4:优化连接管理策略** - **HTTP服务场景**: - 禁用不必要的Keep-Alive:减少长连接占用时间。 - 设置合理的超时时间(如Nginx配置): ```nginx keepalive_timeout 5; # 5秒后关闭空闲连接 ``` --- #### 3. **对比CLOSE_WAIT与TIME_WAIT的差异** | 状态 | 触发方 | 原因 | 解决方案 | |------------|----------|-------------------------------|------------------------------| | CLOSE_WAIT | 被动关闭 | 应用程序未关闭连接 | 修复代码逻辑 | | TIME_WAIT | 主动关闭 | 系统等待延迟报文 | 调整`tcp_tw_reuse`等内核参数[^3] | --- #### 4. **监控与验证** - **实时监控命令**: ```bash watch -n 1 "netstat -ant | grep CLOSE_WAIT | wc -l" # 每秒统计CLOSE_WAIT数量 ``` - **验证修复效果**: - 重启应用后观察CLOSE_WAIT是否减少。 - 使用压力测试工具(如`ab`、`wrk`)模拟高并发场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值