概要描述
在linux系统中遵循一切都是文件的原则,即磁盘文件、目录、网络套接字、磁盘、管道等,都是文件,在我们进行打开的时候会返回一个fd,即是文件句柄。如果频繁的打开文件,或者打开网络套接字而忘记释放就会有句柄泄露的现象。
linux系统中对进程可以调用的文件句柄数进行了限制,如果超过了限制,进程将无法获取新的句柄,而从导致不能打开新的文件,报错too many open files,即句柄数超出系统限制。这里的files不单是文件的意思,也包括打开的通讯链接(比如socket),正在监听的端口等。
详细说明
本文主要从两方面排查,一是系统配置,二是应用层面。
系统配置
根据应用权限,又分为系统级和用户级。系统级的修改会对所有用户有效,而用户级只限制每个登录用户的可连接文件数。
修改用户级别限制
通过命令ulimit -a查看当前系统设置的最大句柄数是多少,open files那一行就代表系统目前允许单个进程打开的最大句柄数,这里是327680。
TDH集群默认是327680,普通情况下linux默认是1024。
然后,查看下当前进程占用句柄的情况:
查看全部进程占用句柄数总和:
lsof | awk '{print $2}' | wc -l
补充
查看某一个进程占用句柄数:
ls -l /proc//fd | wc -l
查看系统中进程打开文件数的排序:
lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | more
如果句柄数占用过多,则可以修改最大句柄数限制,如:
设置最大句柄数为102400
ulimit -SHn 102400
-H:设置硬件资源限制;
-S:设置软件资源限制;
-n size:设置内核可以同时打开的文件描述符的最大值,可以设置的最大值为655350。
这种方式设置后在重启后会还原为默认值。
如果要永久生效,需要修改系统配置文件/etc/security/limits.conf
vim /etc/security/limits.conf
#在最后添加
* soft nofile 102400
* hard nofile 102400
第一列为用户名,*表示所有用户。
hard的设置是实际的默认值,也就是上限;
soft的配置只是用来警告的,如果超过了soft的值,会有warn;
或者只添加
* - nofile 102400
(hard和soft同时配置的方式,是最简单的全局设置。)
修改系统级别限制
上述的ulimit是对进程的限制,对系统总的限制需要设置file-max,可以通过命令来查看fs.file-max的配置:
cat /proc/sys/fs/file-max
或者
sysctl -a
cat /proc/sys/fs/file-max
表示当前内核可以打开的最大的文件句柄数,一般为内存大小(KB)的10%,一般我们不需要主动设置这个值,除非这个值确实较小。
cat /proc/sys/fs/file-nr
12640 0 727680
第一个数字表示当前系统打开的文件数。第三个数字和cat /proc/sys/fs/file-max结果一样表示当前内核可以打开的最大的文件句柄数。
设置的可以分为临时改动和永久改动,临时改动直接可用sysctl -w [变量名]=[值]来解决。例如:
synctl -w fs.file-max=102400
永久改动就需要修改/etc/sysctl.conf文件,如果文件中没有fs.file-max属性,则添加。
设置完成后,使用sysctl -p来加载系统参数,在不指定文件位置的情况下,默认会从/etc/sysctl.conf文件中加载。
应用层面
应用层面一般最常见的原因是连接的泄漏(未正确关闭)导致。
排查过程:
查看close_wait连接数量
netstat -anp | grep CLOSE_WAIT | wc -l
查看具体PID的连接数量(单个节点组件也不会太多,可以逐一排查)
netstat -anp | grep | wc -l
定位到某一个具体的进程后,可以grep 端口,定位泄漏连接的另一端
netstat -anp | grep
例如发现kafka的jmx 9999 端口存在大量的close_wait
netstat -anp | grep 9999
输出如下
tcp6 0 0 :::9999 :::* LISTEN 16030/java
tcp6 0 0 172.16.2.27:9999 172.16.2.27:47155 ESTABLISHED 16030/java
tcp6 0 0 172.16.2.27:47155 172.16.2.27:9999 ESTABLISHED 1147/java
则可判断1147为连接的另一端进程。
ps -ef | grep 1147
输出
root 1147 1 1 8月31 ? 07:24:24 /usr/java/latest/bin/java -agentpath:/usr/lib/transwarp-manager/agent/lib/native/libagent.so -Xms512m -Xmx1024m -XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/transwarp-manager/agent/agent_gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/transwarp-manager/agent -Dfile.encoding=UTF-8 -Dtranswarp.root.logger=INFO, R -Djava.io.tmpdir=/tmp -Djava.library.path=/usr/lib/transwarp-manager/agent/lib/native -Dsun.net.client.defaultConnectTimeout=5000 -Dsun.net.client.defaultReadTimeout=5000 -Dsun.rmi.transport.tcp.handshakeTimeout=10000 -Dsun.rmi.transport.tcp.responseTimeout=15000 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=48121 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -cp /etc/transwarp-manager/agent:/usr/lib/transwarp-manager/agent/lib/*:/usr/lib/transwarp-manager/agent/../common/lib/* io.transwarp.manager.agent.application.TranswarpAgent
root 5101 8751 0 12:27 pts/0 00:00:00 grep --color=auto 1147
得到结论 manager进程到请求kafka的9999 jmx监控端口可能未正确释放,存在问题。
如果netstat查看一切正常,那么可以查看proc目录下具体PID下fd文件数量.(此处的数量过大的进程,同样是异常的)
ls /proc//fd | wc -l
优先定位到具体的组件进程,然后再进一步结合组件日志分析原因。