“EMFILE: too many open files” 常见的建议是调高 ulimit,或者用 graceful-fs。这篇在本机把上限降到 256 复现它:看能打开多少个、哪些读文件的写法真的会触发、日志本身会不会崩溃、fs.watch 为什么也会触发,以及限制并发的效果。
当前 shell 的 ulimit -n(软)= 1048576,sysctl kern.maxfilesperproc = 61440
ulimit -n 256(退出码 0):
第一个失败:EMFILE: too many open files, open '<tmp>/files/f245.txt'
code = EMFILE | errno = -24 | syscall = open
成功打开 245 个,失败 2755 个
同样的循环,但用 console.log 打印错误(stdout 是管道时它要现建一个套接字,也要描述符)(退出码 1):
(libuv) kqueue(): Too many open files
(libuv) kqueue(): Too many open files
node:net:713
throw new ErrnoException(err, 'open');
^
Error: open EMFILE
at new Socket (node:net:713:13)每个进程同时能打开的描述符有上限,node 自己先占了十来个。
ulimit -n 256(同一个进程里既是服务端又是客户端,每个连接占 2 个描述符)(退出码 0):
客户端成功连接 122 个后失败
open EMFILE
code = EMFILE | syscall = open
(libuv) kqueue(): Too many open files连接数或连接泄漏同样会触发 EMFILE;而且描述符用尽时 console.log 可能连错误都打不出来。
promises.readFile:全部成功;0.046 s readFile-callback:全部成功;0.044 s createReadStream:失败,第一个错误 code = EMFILE;0.047 s createReadStream(并发 64):全部成功;0.060 s readFileSync:全部成功;0.052 s
自己管生命周期的 createReadStream、fs.open 一次建太多会触发;限制并发就好。
ulimit -n 64 : 成功 53 个,失败 2947 个,上限与成功数之差 11 ulimit -n 128 : 成功 117 个,失败 2883 个,上限与成功数之差 11 ulimit -n 256 : 成功 245 个,失败 2755 个,上限与成功数之差 11 ulimit -n 1024 : 成功 1013 个,失败 1987 个,上限与成功数之差 11 ulimit -n 4096 : 3000 个全部成功(上限够用)
在启动 node 之前设置;成功数总比上限少 node 自己占的那部分。
ulimit -n 256,监视 1000 个文件:成功监视 245 个后失败:EMFILE: too many open files, watch '<tmp>/files/f245.txt' | code = EMFILE ulimit -n 10240,监视 1000 个文件:监视了 1000 个文件,没有错误
在 macOS 上每个监视的文件一个描述符。