2015-04-19 5 views
2

Я использую узел для рекурсивного прохождения файловой системы и создания системного вызова для каждого файла с помощью child.exec. Он хорошо работает при испытании на небольшой структуры, с парой папок и файлов, но при запуске на весь домашний каталог, он выходит из строя после того, как некоторое времяnodeJS слишком много дочерних процессов?

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) 

произойдет ли это потому, что он использует все ресурсы? Как я могу избежать этого?

EDIT: Код улучшения и лучшие предложения практика всегда приветствуются :)

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(); 
        } 
       ); 
      } 
     }); 
    } 
+1

Пожалуйста, разместите минимальный тестовый пример, который воспроизводит ошибку. Важно понимать код, который вы используете. Также прочитайте модуль [graceful-fs] (https://github.com/isaacs/node-graceful-fs) для модуля изящного fs. ") (Почему он существует и т. Д.). –

+0

Эта ошибка возникает из-за того, что у нее слишком много файлов, но не из-за слишком большого количества процессов. –

+0

. ВСЕГДА получите ответы, если вы включите ваш фактический код, вызывающий проблему. Затем ответы могут быть предоставлены в конкретном контексте того, что вы делаете, и как структурирован ваш код, а не только дает теоретические ответы. – jfriend00

ответ

4

проблема может быть из-за того, много открытых файлов одновременно

рассмотреть возможность использования модуля асинхронного решить вопрос https://github.com/caolan/async#eachLimit

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

В данном примере вы будете обрабатывать 20 файлов одновременно

+0

Я использую async.each, но проблема в том, что это рекурсивная функция, поэтому предел применяется только на каждом уровне каталога. так что вы, скорее всего, превысите предел в любом случае, когда подойдете глубже в кроличью нору – glasspill

+0

@glasspill, если это рекурсивная функция, вы можете просто задержать вызов, чтобы функционировать до обработки некоторых файлов с помощью eachLimit, не нужно проходить все глубокие уровни немедленно. Таким образом, вы можете перемещаться по одному каталогу, обрабатывать файлы, а затем снова вызывать свою рекурсивную функцию, чтобы пересечь следующий каталог –

0

Ну, я не знаю причины неудачи, но если это то, что вы ожидаете (используя все ресурсы) или, как говорят другие (слишком много файлов открываются), вы можете попробовать использовать многозадачность для Это. JXcore (fork of 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.

+0

И как это решает проблему OP? – jfriend00

+0

После того, как он попытается, он может сказать, решает ли это или нет. – infografnet

0

Как было установлено в комментариях, у вас, скорее всего, заканчиваются файловые дескрипторы, потому что вы запускаете слишком много параллельных операций над вашими файлами. Таким образом, решение ограничивает количество одновременных операций, которые запускаются сразу, поэтому слишком много файлов не используются одновременно.

Вот несколько другая реализация, в которой 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"); 
}); 

FYI, я думаю, вы найдете что правильное распространение ошибок и правильная обработка ошибок из многих асинхронных операций намного проще, чем обещание, чем простой метод обратного вызова.

Смежные вопросы