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

错误: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);

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


当前回答

Cwait是一种通用的解决方案,用于限制任何返回承诺的函数的并发执行。

在你的例子中,代码可以是这样的:

var Promise = require('bluebird');
var cwait = require('cwait');

// Allow max. 10 concurrent file reads.
var queue = new cwait.TaskQueue(Promise, 10);
var read = queue.wrap(Promise.promisify(batchingReadFile));

Promise.map(files, function(filename) {
    console.log(filename);
    return(read(filename));
})

其他回答

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

我写的模块是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
        }
    });
});

在运行nodemon命令时也有同样的问题,所以我减少了崇高文本中打开的文件的名称,错误消失了。

还有一种可能性,到目前为止,在任何答案中都没有考虑或讨论过:符号链接循环。

节点的递归文件系统监控器似乎无法检测和处理符号链接的循环。所以你可以很容易地用任意高的nfiles ulimit触发这个错误,只需运行:

mkdir a
mkdir a/b
cd a/b 
ln -s .. c

GNU find会注意到符号链接循环并中止:

$ find a -follow
a
a/b
find: File system loop detected; ‘a/b/c’ is part of the same file system loop as ‘a’.

但是节点不会。如果你在树上设置了一个手表,它会抛出一个EMFILE,太多打开文件的错误。

在有包含关系的node_modules中也会发生这种情况:

parent/
  package.json
  child/
    package.json

这也是我在做项目时遇到的问题。

我有这个问题,我通过运行npm更新解决了它,它工作了。

在某些情况下,可能需要删除node_modules rm -rf node_modules/

对于非恶魔用户: 只需使用——ignore标志来解决问题。

例子:

nodemon app.js --ignore node_modules/ --ignore data/