朕喜技术(ZhenxiJS)
2026.08.27
1 分钟阅读

服务器报 "Too many open files" 崩溃?文件描述符(File Descriptor)耗尽排查与解决

关键词:Too many open files、Errno 24、EMFILE、file descriptor、文件描述符耗尽、 ulimit -n、nofile、limits.conf、LimitNOFILE、docker compose ulimits、高并发服务器崩溃、 accept 报错、lsof -p、/proc/pid/limits

问题背景

高并发 Web 服务在连接数升高后突然崩溃,报 [Errno 24] Too many open files, 即使 内存还有几 GB、磁盘空间充足,也 accept() 直接失败、无法接受新连接。 用 lsof -p <pid> 一看,进程打开的文件描述符(多为 socket)已占满上限。

这是服务器运维里极其经典且高频的问题:文件描述符(File Descriptor)耗尽。

复现步骤

# 用 docker 起一个低 ulimit 环境(模拟默认 1024 限制)
docker compose up -d --build
docker compose exec app bash

# 1) 查看当前 fd 限制
ulimit -n          # soft limit(通常是 1024)
ulimit -Hn         # hard limit
cat /proc/sys/fs/file-max   # 系统级最大 fd

# 2) 打开大量文件触发 EMFILE
python3 fd_exhaust.py
# 输出:❌ 在打开第 1022 个文件时失败:[Errno 24] Too many open files

# 3) 高并发服务器崩溃
python3 server.py 8080 &          # 启动服务器
bash stress_test.sh 8080 1500 3   # 并发 1500 个长连接
# 输出:❌ 服务器崩溃![Errno 24] Too many open files,达到文件描述符上限

根本原因

Linux 对每个进程的文件描述符数量做了限制(ulimit -n,默认仅 1024)。 高并发服务需要同时打开成百上千个 socket,一旦超过限制,内核拒绝分配新的 fd, open() / accept() 就会抛 EMFILE: Too many open files。

关键点:fd 限制与内存、磁盘无关,所以"资源充足但连不上"是典型表象。

解决办法(按场景选一种)

方案 A:docker-compose(本项目采用,永久、声明式)

docker-compose.yml 中给服务加:

ulimits:
  nofile:
    soft: 65535
    hard: 65535

改完 docker compose down && docker compose up -d 生效。

方案 B:临时生效(当前 shell,重启失效)

ulimit -n 65535

方案 C:永久生效(系统 / systemd)

  • /etc/security/limits.conf 或 /etc/security/limits.d/*.conf:
    * soft nofile 65535
    * hard nofile 65535
    
  • systemd 服务 unit:
    [Service]
    LimitNOFILE=65535
    
  • 运行时:docker run --ulimit nofile=65535:65535 ...

验证

  • ulimit -n → 65535
  • cat /proc/<pid>/limits → 查看进程实际 Max open files
  • fd_exhaust.py → 能打开 6 万+ 个文件(远超 1024)
  • 压测 3000 并发长连接 → 服务器正常存活,不再崩溃

经验与避坑

  1. 注意 Docker 版本差异:较新 Docker(v29+)容器默认 nofile 就是 65535, 若不显式调低,问题不会复现。复现"经典 1024 限制"要用 ulimits: nofile: 1024:1024。
  2. os.getrlimit 不存在:Python 取 fd 限制要用 resource.getrlimit(resource.RLIMIT_NOFILE)。
  3. 单线程同步 accept + 立即 close 复现不了 fd 耗尽:连接处理完就关、不累积; 需让服务器保持连接打开(模拟长连接/长轮询/慢客户端)才能在进程内累积并发 socket 并触发 EMFILE。
  4. b"..." 与 f-string 隐式拼接会报错:SyntaxError: cannot mix bytes and nonbytes literals, 应写成 b"..." + f"...".encode()。
  5. 重定向日志看不到输出:nohup python3 ... > log 2>&1 下 stdout 有缓冲, 用 print(..., flush=True) 保证实时可见。
  6. 排查命令速查:ulimit -n、cat /proc/<pid>/limits、lsof -p <pid> | wc -l、 cat /proc/sys/fs/file-nr(系统已用/空闲 fd)。

技术栈

Linux、Docker / docker-compose、Python3、Nginx/Node 等 Web 服务、systemd、EMFILE 错误处理。

版权声明

本文为博主原创文章,遵循 CC BY-NC-SA 4.0 许可协议。 转载请注明出处:http://localhost/blogs/2026-08-27-1600-too-many-open-files

分享这篇文章