emqx、MongoDB或者java程序,出现 Too many open files 问题
"Too many open files"(太多打开的文件)是一个常见的系统错误,表示一个进程已经达到了它所能打开的最大文件描述符(file descriptor)数量限制。这个错误在类 Unix 系统(如 Linux 和 macOS)中尤为常见。
错误表现形式
当你遇到这个问题时,可能会看到如下错误信息:
- Java 应用:
java.io.IOException: Too many open files
- Node.js 应用:
EMFILE: too many open files
- Python、Nginx、MySQL 等也都会抛出类似的错误。
- 系统层面可能显示:
open() failed (24: Too many open files)
- EMQX和MongoDB:t
oo many open files
原因分析
1. 文件描述符耗尽
每个打开的文件、网络连接、管道等在操作系统中都对应一个“文件描述符”(File Descriptor,简称 fd)。操作系统和进程本身对可以打开的文件描述符数量有限制。
2. 资源泄漏(常见原因)
- 程序打开了文件、Socket 或数据库连接但没有正确关闭。
- 多线程程序中未正确释放资源。
- 使用异步 IO 或事件驱动模型时忘记清理句柄。
3. 系统或用户设置过低
- 默认的
ulimit
设置可能太小,无法支撑高并发应用。
排查与解决
1. 查看当前限制
使用以下命令查看当前 shell 的文件描述符限制:
ulimit -n
输出是 1024
,是默认值。
2. 查看某个进程的已打开文件数
假设你要查 PID 是 1234
的进程:
lsof -p 1234 | wc -l
或者
ls /proc/1234/fd | wc -l
这将告诉你该进程当前打开了多少个文件描述符。
3. 修改系统限制(临时)
ulimit -n 65536
注意:这只是当前会话有效,重启后失效。
4. 永久修改 ulimit
编辑 /etc/security/limits.conf
文件,添加:
* soft nofile 65536
* hard nofile 65536
如果是特定用户(比如 tomcat
):
tomcat soft nofile 65536
tomcat hard nofile 65536
还要确保 PAM 配置启用 limits:
检查 /etc/pam.d/common-session
是否有:
session required pam_limits.so
5. 修改 systemd 服务的限制(适用于使用 systemd 的系统)
如果你的服务是通过 systemd 启动的(如 Nginx、Java 应用),需要在服务单元文件中添加:
[Service]
LimitNOFILE=65536
然后重载 systemd:
systemctl daemon-reexec
systemctl restart your-service
优化建议
- 定期检查代码中是否及时关闭了文件、Socket、数据库连接等。
- 对于高并发服务器,建议将最大文件描述符设为
65536
或更高。 - 使用连接池管理数据库连接、HTTP 连接等。
- 使用工具如
lsof
、strace
、netstat
来定位资源泄露点。
示例场景
场景 | 可能原因 |
---|---|
Web 服务器(如 Nginx、Tomcat)报错 | 并发太高,fd 不够;或存在连接未关闭 |
Java 应用报错 | 打开太多文件或 Socket 没有关闭 |
数据库连接池问题 | 没有释放连接,导致 socket 资源泄漏 |
小技巧:模拟 Too Many Open Files
你可以用下面的小脚本来测试这个错误:
#!/bin/bash
for i in {1..10000}; doexec 200<$i
done
运行后就会触发 "Too many open files" 错误。