一、现象
linux系统中运行了一个TCP服务器,该服务器监听的TCP端口为10000。但是长时间运行时发现该端口会崩溃,TCP客户端连接该端口会失败:
可以看到进行三次握手时,TCP客户端向该TCP服务器的10000端口发送了SYN报文,但是TCP服务器返回了RST报文。此时我们用netstat命令查看到10000端口没有被监听了,这意味着该端口被关闭或者不在监听中了。
二、排查思路
服务器发送RST包的原因可能有以下几种:
连接重置:当服务器主动关闭连接时,它会发送RST包。
收到非法数据包:当服务器收到一个不符合协议规则的数据包时,它可能会发送RST包。
防火墙规则:服务器的防火墙规则可能会拦截特定的数据包,并发送RST包。
连接拒绝:当服务器拒绝客户端的连接请求时,它可能会发送RST包。
其他原因:可能是由于网络硬件故障或其他原因导致的。
一开始我们以为是服务器的网络架构原因导致端口被关闭。但我们注意到端口被关闭时日志不断打印信息:“msg:socket(): Too many open files”。出现这个报错的大多数情况都是文件句柄(file handle)泄露,通俗的说就是文件句柄在不断的被打开,但是在使用完成之后却没有正常的关闭导致文件打开数不断的增加。文件句柄泄露有多种原因,而不仅仅是打开文件,常见的来源有:套接字,管道,数据库连接,文件。这可能意味着我们的程序打开了太多文件没有关闭导致同时打开的文件数超出了系统的限制。
为了验证我们的猜想,我们不断查看服务器进程目前打开的句柄数量:lsof -p 进程ID | wc -l
可以看到服务器进程打开的句柄数量不断上升,说明确实是因为服务器进程在某个时刻打开了超过系统限制的文件数量或者通讯链接数导致的。
阅读服务器代码:
可以看到上述代码中,调用了fopen函数后,没有调用对应的fclose函数进行文件的关闭,所以导致了该问题的产生。我们把fclose函数加上去,即解决了该问题。
这次产生上述“msg:socket(): Too many open files”报错的原因是由于我们的代码有bug导致,但很多时候,我们的进程就是需要打开很多文件或者通讯链接,所以这时候我们得要修改全局配置,将系统允许的程序最大打开文件数调大。
通过“ ps -ef | grep 进程名称” 命令可以得到进程id,然后我们通过命令
cat /proc/进程id/limits
可以查看到该进程最大open files限制
可以看到该进程最大的文件打开数量,软限制是1024,硬限制是4096。关于软限制和硬限制的区别:
软限制可以在程序的进程中自行改变(突破限制),而硬限制则不行(除非程序进程有root权限)
我们修改全局配置文件,将DefaultLimitNOFILE修改为100000,DefaultLimitNPROC修改为65535
vim /etc/systemd/system.conf
然后保存,reboot重启linux系统(或者执行systemctl daemon-reexec命令使得system.conf
修改生效方法),重启我们的进程,重新查看该进程最大open files数
cat /proc/进程id/limits
可以看到最大open files数已经被改了
这样,即使我们的进程打开很多文件或者通讯链接,端口也不会崩溃了
三、参考
Too many open files (CLOSE_WAIT过多)的解决方案:修改打开文件数的上限值、调整TCP/IP的参数 原创
程序 too many open files 问题排查及解决