报错场景:使用socket做文件传输,运行中报错 too many open file
Too many open files是Linux系统中常见的错误,从字面意思上看就是说程序打开的文件数过多,不过这里的files不单是文件的意思,也包括打开的通讯链接(比如socket),正在监听的端口等等,所以有时候也可以叫做句柄(handle),这个错误通常也可以叫做句柄数超出系统限制。引起的原因就是进程在某个时刻打开了超过系统限制的文件数量以及通讯链接数。
linux 服务器上执行程序down机,查看log日志发现panic :too many open file
当你的服务器在大并发达到极限时,就会报出“too many open files”。
1.1 查看线程占句柄数
运行 ulimit -a 查看我们open file的文件数量
其中
open files (-n) 1024 代表每个
1.2 查看系统打开句柄最大数量
more /proc/sys/fs/file-max
1.3 查看打开句柄总数
lsof|awk '{print $2}'|wc -l
1.4 根据打开文件句柄的数量降序排列,其中第二列为进程ID:
lsof|awk '{print $2}'|sort|uniq -c|sort -nr|more
1.5 查看某个进程目前打开的句柄数量
lsof -p 进程ID | wc -l
1.6 根据获取的进程ID查看进程的详情
ps -ef |grep
1 2 3 4 |
# 临时修改
或 # 永久修改
|
2.2. 修改“进程级”的 ulimit 的open file,系统默认的ulimit对文件打开数量的限制是1024
1 2 3 4 5 |
或 # 永久修改
修改以后保存,注销当前用户,重新登录,执行ulimit -a 查看是否生效 |
注意:
附录1.
为了让一个程序的open files数目扩大,可以在启动脚本前面加上ulimit -HSn 102400命令。但当程序是一个daemon时,可能这种方法无效,因为没有终端。
附录2.
如果某项服务已经启动,再动态调整ulimit是无效的,特别是涉及到线上业务就更麻烦了。
这时,可以考虑通过修改/proc/’程序pid’/limits来实现动态修改!!!
此外,如果用supervisor托管和启动项目,会遇到这个配置无法生效的问题,原因在于supervisor会默认配置打开的句柄数量是1024,
如果要查看某个进程最大open files,可以通过这个进程的进程号对应的limits查看
cat /proc/进程ID/limits
其中有一行是:
Max open files 1024 1024 bytes
supervisor托管的程序这一行默认都是supervisor配置的最大数量1024,这时需要手动改一下supervisor的配置文件,修改方式如下,以Ubuntu系统为例,找到supervisor的配置文件supervisord.conf
在[supervisord]选项中,增加minfds选项的配置
[supervisord]
minfds=65535 ; min. avail startup file descriptors; default 1024
配置完毕后,需要重启supervisor(以systemctl为例)
systemctl restart supervisor
即可生效
为什么需要修改两个参数和open file报错发生的根本原因? file-max和limits.conf中的两个参数?
我们已经了解到想要解决用户出现“too many open files”的问题需要同时修改用户级参数文件limits.conf及系统级参数文件sysctl.conf,那么怎么修改才合理呢?怎么修改才能万无一失呢?文章开始我提到,在最初出现“too many open files”问题的时候,我们只是简单粗暴的在nofile默认值和fs.file-max默认值后面加一个零甚至几个零,为什么会出现有时能解决问题,有时又无济于事的情况呢?我根据日常维护经验总结出了参数值设置需要遵循的以下几个原则:
1、根据limits.conf 中的soft nofile和hard nofile的定义:soft 指的是当前系统生效的设置值;hard 表明系统中所能设定的最大值,可以看出,soft nofile必须小于等于hard nofile值。2、用户级打开文件数参数nofile必须小于等于系统级打开文件数参数fs.file-max。目前X86平台linux默认nofile为5000,fs.file-max为4096,在我们简单粗暴的在nofile默认值后增加几个零,一旦nofile值大于fs.file-max值时,打开文件数仍然会以fs.file-max值为准,也就是4096,仍然小于应用程序所需要的打开文件数,这就合理解释了为什么已经扩大了nofile值却仍然无法解决“too many open files”的问题了。
3、fs.file-max*4k 必须小于内存(字节数)。看到这个公式也许你会有些头晕,那么我来拿4G内存来举个简单的例子。如一台虚拟机内存为4G(4*1024*1024字节),根据公式算出fs.file-max必须小于1024*1024,约100万。也就是说拿一台4G内存的虚拟机来说,fs.file-max值设置低于100万是基于硬件条件下合理的数值;如果设置超出了100万的值,那么相当于对系统打开文件数无限制,没有预警机制,当系统打开文件数超过100万个时,系统将会夯死。换句话说,如果在当前内存下允许的最大文件打开数还是无法符合应用程序要求的话,只能使用增加内存的方式来提高性能了。
4、nofile最大值不可设置大于102万,大于102万的话会造成用户无法登陆。这个原则来自资深红帽系统工程师的口述,大家记住就好了。
注意:修改limits.conf 文件时候要根据“ 系统核数 ”进行修改,不能随便写,一般按照4核算的话 (4*1024*1024) 虚拟内存就这么大,大约一百万左右,你的file-max limit.conf中的那两个值就不能比这个大
如果写的比虚拟内存还打的后果就是sudo 登陆不上,系统无法打开新的文件,因为linux系统一切皆文件,所以后果会很难受,一般执行sudo的时候会有这个报错
sudo: pam_open_session: Permission denied
sudo: policy plugin failed session initialization
详情如下:
一次修改limits.conf 引发的血案
起因:
在重启nginx 过程中,发现有报 open file 限制的警告,于是没考虑太多,直接去修改/etc/security/limits.conf修改前
* soft nofile 65536 * hard nofile 65536
修改后
* soft nofile 6553600 * hard nofile 6553600
保存退出,发现无论是复制窗口还是重新登录都会失败,因为是使用的普通账号,在执行sudo 和su 的时候也会报错如下
sudo: pam_open_session: Permission denied sudo: policy plugin failed session initialization
原因:
/etc/security/ulimits.conf 文件中nofile 的软硬限制的值不能超过内核参数 /proc/sys/fs/nr_open ,否则就有问题
解决方案
- 如果已经重启启动,那没有没法了,只能重启,进入单用户模式,然后将limits.conf 修改为正常的
- 如果只有一个普通账号还登录着,也没有办法了,因为sudo也用不了,只能重启,单用户
- 如果还有会话使用root 用户登录着,可以直接用root修改回来,或者root把 /proc/sys/fs/nr_open 内核参数调大
关于linux上默认资源限制的一些参考知识
ulimit
ulimit 是用来限制用户在当前会话的资源限制的,若新开一个会话,则用户的会话级资源限制又会回复到默认值。如果要想让用户的资源限制的修改同步到用户的每个限制,有两种做法
- 写入用户家目录的 profile or bashrc 中
- 修改limits.conf
limits.conf
关于limits.conf 中nofile 的修改需要慎重,虽然根据man 手册,可以设置值为 -1,unlimited or infinity indicating no limit,但如果设置为-1,则会出现文章之前出现的问题。我这里还只是将nofile的值修改的太大就出现问题。
内核参数
具体的内核参数的解释可以查看http://man7.org/linux/man-pages/man5/proc.5.html
/proc/sys/fs/file-max
- 操作系统级别的限制,所有进程打开的文件数之和不能超过这个值
/proc/sys/fs/file-nr
- 操作系统级别的参数,查看当前已经打开的文件数以及操作系统允许的最大值
/proc/sys/fs/nr_open
- 进程级别的参数,限制每个进程能打开的最大文件数
一次修改limits.conf 引发的血案 - 简书(来源)
请教下各位老哥,我有一个单节点的ES,总共有将近一千万条数据,索引有42个。
然后最近测试的同事反映ES占用文件句柄数太大:
但是我执行 /_nodes/stats/process 后,发现打开的文件数又很少:
"open_file_descriptors" : 1447,
"max_file_descriptors" : 1024000
1447对比70万,也差了太远了吧……
究竟哪一个才是正确的呢?
另外如果70万是正确的,单节点ES为什么会有这么大的文件句柄占用呢?请教下各位有没有什么可以考虑优化的点。
segments的数量我有定期合并,目前看segements的数量大概200多个。
解决方案:
使用lsof -n命令获得的结果是非常不准确的,它会把一些并没有占用句柄数的文件也一并计算在内。
使用ES API查到的数据是准确的。
再补充一下在linux查询进程占用fd比较准确的方法吧:ls -l /proc//fd | wc -l