nodeJS 子进程太多?

nodeJS too many child processes?

我正在使用节点递归遍历文件系统并对每个文件进行系统调用,方法是 child.exec。它在包含几个文件夹和文件的小型结构上测试时运行良好,但在整个主目录上 运行 时,它会在一段时间后崩溃

child_process.js:945
throw errnoException(process._errno, 'spawn');
      ^
Error: spawn Unknown system errno 23
at errnoException (child_process.js:998:11)
at ChildProcess.spawn (child_process.js:945:11)
at exports.spawn (child_process.js:733:9)
at Object.exports.execFile (child_process.js:617:15)
at exports.exec (child_process.js:588:18)

发生这种情况是因为它耗尽了所有资源吗?我怎样才能避免这种情况?

编辑:代码 随时欢迎改进和最佳实践建议:)

    function processDir(dir, callback) {
        fs.readdir(dir, function (err, files) {
            if (err) {...}
            if (files) {
                async.each(files, function (file, cb) {
                        var filePath = dir + "/" + file;
                        var stats = fs.statSync(filePath);
                        if (stats) {
                            if (stats.isFile()) {
                                processFile(dir, file, function (err) {
                                    if (err) {...}
                                    cb();
                                });
                            } else if (stats.isDirectory()) {
                                processDir(filePath, function (err) {
                                    if (err) {...}
                                    cb();
                                });
                            }
                        }
                    }, function (err) {
                        if (err) {...}
                        callback();
                    }
                );
            }
        });
    }

问题可能是因为同时打开了很多文件

考虑使用异步模块来解决问题 https://github.com/caolan/async#eachLimit

async.eachLimit(
  files,
  20,
  function(file, callback){
    //process file here and call callback
  },
  function(err){
     //done
  }
);

在当前示例中,您将一次处理 20 个文件

好吧,我不知道失败的原因,但如果这是您所期望的(用完所有资源)或如其他人所说(打开的文件太多),您可以尝试使用多任务处理为了它。 JXcore(Node.JS 的分支)提供了这样的东西 - 它允许 运行 在单独的实例中执行一项任务,但这仍然是在一个进程中完成的。

虽然 Node.JS 应用程序作为一个进程有其局限性 - JXcore 及其子实例使这些限制倍增:单个进程即使有一个额外的实例(或任务,或者好吧:我们可以称之为子线程) 将限制加倍!

那么,假设您将 运行 每个 spawn() 都放在一个单独的任务中。或者,由于任务不再 运行 在主线程中运行 - 您甚至可以使用 jxcore 提供的同步方法:cmdSync().

这几行代码可能是最好的说明:

jxcore.tasks.setThreadCount(4);

var task = function(file) {
  var your_cmd = "do something with " + file;
  return jxcore.utils.cmdSync(your_cmd);
};

jxcore.tasks.addTask(task, "file1.txt", function(ret) {
  console.log("the exit code:", ret.exitCode);
  console.log("output:", ret.out);
});

让我重复一遍:任务不会阻塞主线程,因为它 运行在一个单独的实例中!

多任务处理 API 记录在此处:Multitasking

如评论中所述,您可能 运行 正在用完文件句柄,因为您 运行 对文件进行了太多并发操作。因此,一个解决方案是限制一次 运行 的并发操作数量,因此有太多文件未同时使用。

这里有一个稍微不同的实现,它使用 Bluebird 承诺来控制操作的异步方面和操作的并发方面。

为了简化并发方面的管理,这首先将整个文件列表收集到一个数组中,然后处理文件名数组,而不是边处理边处理。这使得在 Bluebird 的 .map()(在单个数组上工作)中使用内置并发功能变得更加容易,因此我们不必自己编写该代码:

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var path = require("path");

// recurse a directory, call a callback on each file (that returns a promise)
// run a max of numConcurrent callbacks at once
// returns a promise for when all work is done
function processDir(dir, numConcurrent, fileCallback) {
    var allFiles = [];

    function listDir(dir, list) {
        var dirs = [];
        return fs.readdirAsync(dir).map(function(file) {
            var filePath = path.join(dir , file);
            return fs.statAsync(filePath).then(function(stats) {
                if (stats.isFile()) {
                    allFiles.push(filePath);
                } else if (stats.isDirectory()) {
                    return listDir(filePath);
                }
            }).catch(function() {
                // ignore errors on .stat - file could just be gone now
                return;
            });
        });
    }

    return listDir(dir, allFiles).then(function() {
        return Promise.map(allFiles, function(filename) {
            return fileCallback(filename);
        }, {concurrency: numConcurrent});
    });
}

// example usage:

// pass the initial directory, 
// the number of concurrent operations allowed at once
// and a callback function (that returns a promise) to process each file
processDir(process.cwd(), 5, function(file) {
    // put your own code here to process each file
    // this is code to cause each callback to take a random amount of time 
    // for testing purposes
    var rand = Math.floor(Math.random() * 500) + 500;
    return Promise.delay(rand).then(function() {
        console.log(file);
    });
}).catch(function(e) {
    // error here
}).finally(function() {
    console.log("done");
});

仅供参考,我认为您会发现许多异步操作的正确错误传播和正确错误处理比普通回调方法要容易得多。