节点和错误:EMFILE,打开的文件太多

IT技术 javascript node.js macos
2021-01-15 13:53:03

几天来,我一直在寻找解决错误的有效解决方案

错误:EMFILE,打开的文件太多

似乎很多人都有同样的问题。通常的答案涉及增加文件描述符的数量。所以,我试过这个:

sysctl -w kern.maxfiles=20480

默认值是10240。这在我看来有点奇怪,因为我在目录中处理的文件数在10240以下。更奇怪的是,我增加文件描述符数后仍然收到同样的错误.

第二个问题:

经过多次搜索,我找到了解决“打开文件太多”问题的方法:

var requestBatches = {};
function batchingReadFile(filename, callback) {
  // First check to see if there is already a batch
  if (requestBatches.hasOwnProperty(filename)) {
    requestBatches[filename].push(callback);
    return;
  }

  // Otherwise start a new one and make a real request
  var batch = requestBatches[filename] = [callback];
  FS.readFile(filename, onRealRead);
  
  // Flush out the batch on complete
  function onRealRead() {
    delete requestBatches[filename];
    for (var i = 0, l = batch.length; i < l; i++) {
      batch[i].apply(null, arguments);
    }
  }
}

function printFile(file){
    console.log(file);
}

dir = "/Users/xaver/Downloads/xaver/xxx/xxx/"

var files = fs.readdirSync(dir);

for (i in files){
    filename = dir + files[i];
    console.log(filename);
    batchingReadFile(filename, printFile);

不幸的是,我仍然收到同样的错误。这段代码有什么问题?

6个回答

优雅的 fs不起作用时……或者您只想了解泄漏的来源。按照这个过程。

(例如,如果您的问题与套接字有关,graceful-fs 不会修复您的旅行车。)

来自我的博客文章:http : //www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

如何隔离

此命令将输出 nodejs 进程的打开句柄数:

lsof -i -n -P | grep nodejs
COMMAND     PID    USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
...
nodejs    12211    root 1012u  IPv4 151317015      0t0  TCP 10.101.42.209:40371->54.236.3.170:80 (ESTABLISHED)
nodejs    12211    root 1013u  IPv4 151279902      0t0  TCP 10.101.42.209:43656->54.236.3.172:80 (ESTABLISHED)
nodejs    12211    root 1014u  IPv4 151317016      0t0  TCP 10.101.42.209:34450->54.236.3.168:80 (ESTABLISHED)
nodejs    12211    root 1015u  IPv4 151289728      0t0  TCP 10.101.42.209:52691->54.236.3.173:80 (ESTABLISHED)
nodejs    12211    root 1016u  IPv4 151305607      0t0  TCP 10.101.42.209:47707->54.236.3.172:80 (ESTABLISHED)
nodejs    12211    root 1017u  IPv4 151289730      0t0  TCP 10.101.42.209:45423->54.236.3.171:80 (ESTABLISHED)
nodejs    12211    root 1018u  IPv4 151289731      0t0  TCP 10.101.42.209:36090->54.236.3.170:80 (ESTABLISHED)
nodejs    12211    root 1019u  IPv4 151314874      0t0  TCP 10.101.42.209:49176->54.236.3.172:80 (ESTABLISHED)
nodejs    12211    root 1020u  IPv4 151289768      0t0  TCP 10.101.42.209:45427->54.236.3.171:80 (ESTABLISHED)
nodejs    12211    root 1021u  IPv4 151289769      0t0  TCP 10.101.42.209:36094->54.236.3.170:80 (ESTABLISHED)
nodejs    12211    root 1022u  IPv4 151279903      0t0  TCP 10.101.42.209:43836->54.236.3.171:80 (ESTABLISHED)
nodejs    12211    root 1023u  IPv4 151281403      0t0  TCP 10.101.42.209:43930->54.236.3.172:80 (ESTABLISHED)
....

请注意: 1023u(最后一行) - 这是默认最大值的第 1024 个文件句柄。

现在,看看最后一列。这表明哪个资源是开放的。您可能会看到许多行都具有相同的资源名称。希望现在可以告诉您在代码中查找泄漏的位置。

如果你不知道多个节点进程,首先查找哪个进程有pid 12211。它会告诉你进程。

在我上面的例子中,我注意到有一堆非常相似的 IP 地址。他们都是54.236.3.### 通过进行 ip 地址查找,能够确定在我的情况下它与 pubnub 相关。

命令参考

使用此语法来确定进程打开了多少个打开的句柄...

获取某个 pid 的打开文件数

我使用此命令来测试在我的应用程序中执行各种事件后打开的文件数。

lsof -i -n -P | grep "8465" | wc -l
# lsof -i -n -P | grep "nodejs.*8465" | wc -l
28
# lsof -i -n -P | grep "nodejs.*8465" | wc -l
31
# lsof -i -n -P | grep "nodejs.*8465" | wc -l
34

你的进程限制是多少?

ulimit -a

您想要的行将如下所示:

open files                      (-n) 1024

永久更改限制:

  • 在 Ubuntu 14.04、nodejs v. 7.9 上测试

如果您希望打开许多连接(websockets 就是一个很好的例子),您可以永久增加限制:

  • 文件:/etc/pam.d/common-session (添加到最后)

      session required pam_limits.so
    
  • 文件:/etc/security/limits.conf (添加到最后,如果已经存在则编辑)

      root soft  nofile 40000
      root hard  nofile 100000
    
  • 重新启动您的 nodejs 并从 ssh 注销/登录。

  • 这可能不适用于较旧的 NodeJS,您需要重新启动服务器

  • 如果您的节点使用不同的 uid 运行,请使用代替。

由于@blak3r 的博客似乎已关闭,这里是他关于回程机的文章的链接。web.archive.org/web/20140508165434/http://... 超级有用,非常棒的阅读!
2021-03-22 13:53:03
我有稀有号码。lsof -i -n -P | grep "12843" | wc -l== 4085ulimit -a | grep "open files"== (-n) 1024 有什么线索可以打开比最大限制更多的文件吗?
2021-04-06 13:53:03
这是最具描述性和正确性的答案。谢谢!
2021-04-07 13:53:03
如何更改打开文件限制?
2021-04-13 13:53:03
ulimit -n 2048 允许打开 2048 个文件
2021-04-14 13:53:03

使用graceful-fsIsaac Schlueter(node.js 维护者)module可能是最合适的解决方案。如果遇到 EMFILE,它会进行增量回退。它可以用作内置fsmodule的直接替代品

你怎么npm呢?我如何在我的代码中组合它而不是常规的 fs?
2021-03-17 13:53:03
如果它是您自己的代码,那很好,但是很多 npm module不使用它。
2021-03-18 13:53:03
我认为,一般来说,Node 会尽量向用户公开。这让每个人(不仅仅是 Node 核心开发人员)都有机会解决因使用这个相对原始的界面而产生的任何问题。同时,发布解决方案,通过npm下载别人发布的解决方案也非常方便。不要指望 Node 本身有很多聪明才智。相反,期望在 npm 上发布的包中找到智能。
2021-03-20 13:53:03
这个module解决了我所有的问题!我同意 node 似乎仍然有点原始,但主要是因为很难理解这么少的文档出了什么问题,并且已经接受了已知问题的正确解决方案。
2021-03-21 13:53:03
救了我,为什么这不是节点默认值?为什么我需要安装一些第三方插件来解决这个问题?
2021-04-01 13:53:03

我不确定这是否会帮助任何人,我开始从事一个有很多依赖项的大项目,这给我带来了同样的错误。我的同事建议我watchman使用 brew安装,这为我解决了这个问题。

brew update
brew install watchman

2019 年 6 月 26 日编辑: Github 链接到守望者

@bh4r4th 我尝试brew update; brew reinstall watchman了所有可能的方式。对我来说,作为 M1 用户,有效的是更改使用哪个节点二进制 xcode。
2021-03-20 13:53:03
@Slackware 你能更清楚地评论你在这里的评论吗?您是在尝试解释您的问题还是在谈论解决方案?
2021-03-22 13:53:03
这个对我有用!mac m1 大苏尔
2021-03-25 13:53:03
这至少对我有帮助。在 react-native 项目中,bundler 可以本地打开文件,或者(如果已安装)使用 watchman 以对操作系统更好的方式来完成。所以它可以是一个很大的帮助 - 它甚至记录在 macOS 的 react-native CLI 快速入门中:facebook.github.io/react-native/docs/getting-started.html - 干杯!
2021-04-05 13:53:03
我做了which node我去了 XCode,我的应用程序,构建阶段,找到Bundle React Native code and images并更改nodewhich node.
2021-04-13 13:53:03

您正在阅读太多文件。节点异步读取文件,它将一次读取所有文件。因此,您可能正在阅读 10240 限制。

看看这是否有效:

var fs = require('fs')
var events = require('events')
var util = require('util')
var path = require('path')

var FsPool = module.exports = function(dir) {
    events.EventEmitter.call(this)
    this.dir = dir;
    this.files = [];
    this.active = [];
    this.threads = 1;
    this.on('run', this.runQuta.bind(this))
};
// So will act like an event emitter
util.inherits(FsPool, events.EventEmitter);

FsPool.prototype.runQuta = function() {
    if(this.files.length === 0 && this.active.length === 0) {
        return this.emit('done');
    }
    if(this.active.length < this.threads) {
        var name = this.files.shift()

        this.active.push(name)
        var fileName = path.join(this.dir, name);
        var self = this;
        fs.stat(fileName, function(err, stats) {
            if(err)
                throw err;
            if(stats.isFile()) {
                fs.readFile(fileName, function(err, data) {
                    if(err)
                        throw err;
                    self.active.splice(self.active.indexOf(name), 1)
                    self.emit('file', name, data);
                    self.emit('run');

                });
            } else {
                self.active.splice(self.active.indexOf(name), 1)
                self.emit('dir', name);
                self.emit('run');
            }
        });
    }
    return this
};
FsPool.prototype.init = function() {
    var dir = this.dir;
    var self = this;
    fs.readdir(dir, function(err, files) {
        if(err)
            throw err;
        self.files = files
        self.emit('run');
    })
    return this
};
var fsPool = new FsPool(__dirname)

fsPool.on('file', function(fileName, fileData) {
    console.log('file name: ' + fileName)
    console.log('file data: ', fileData.toString('utf8'))

})
fsPool.on('dir', function(dirName) {
    console.log('dir name: ' + dirName)

})
fsPool.on('done', function() {
    console.log('done')
});
fsPool.init()

我今天遇到了这个问题,并没有找到好的解决方案,我创建了一个module来解决它。我受到@fbartho 片段的启发,但想避免覆盖 fs module。

我写的module是Filequeue,你可以像 fs 一样使用它:

var Filequeue = require('filequeue');
var fq = new Filequeue(200); // max number of files to open at once

fq.readdir('/Users/xaver/Downloads/xaver/xxx/xxx/', function(err, files) {
    if(err) {
        throw err;
    }
    files.forEach(function(file) {
        fq.readFile('/Users/xaver/Downloads/xaver/xxx/xxx/' + file, function(err, data) {
            // do something here
        }
    });
});