узел и ошибка: EMFILE, слишком много открытых файлов - PullRequest
136 голосов
/ 23 января 2012

В течение нескольких дней я искал рабочее решение с ошибкой

Error: EMFILE, too many open files

Кажется, у многих людей такая же проблема. Обычный ответ включает в себя увеличение количества файловых дескрипторов. Итак, я попробовал это:

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);

К сожалению, я все еще получаю ту же ошибку. Что не так с этим кодом?

Последний вопрос (я новичок в javascript и нодах), я нахожусь в процессе разработки сети приложение с большим количеством запросов около 5000 ежедневных пользователей. У меня много лет опыта в программирование с другими языками, такими как Python и Java. поэтому первоначально я подумал о разработке этого приложения с Django или Play Framework. Затем я обнаружил узел и должен сказать, что идея неблокирующей модели ввода / вывода действительно хороша, соблазнительна и, скорее всего, очень быстра!

Но каких проблем мне ожидать с узлом? Это проверенный производственный веб-сервер? Каковы ваши переживания?

Ответы [ 10 ]

73 голосов
/ 12 января 2014

Когда graceful-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.

Справочник команд

Используйте этот синтаксис, чтобы определить, сколько открытых дескрипторов имеет процессopen ...

Чтобы получить количество открытых файлов для определенного 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.
68 голосов
/ 10 апреля 2013

Использование модуля graceful-fs Исаака Шлютера (сопровождающий node.js), вероятно, является наиболее подходящим решением.Это делает постепенный откат, если встречается EMFILE.Может использоваться в качестве замены для встроенного модуля fs.

6 голосов
/ 08 марта 2013

Я столкнулся с этой проблемой сегодня, и, не найдя хороших решений, я создал модуль для ее решения. Я был вдохновлен фрагментом @ 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
        }
    });
});
5 голосов
/ 23 января 2012

Вы читаете слишком много файлов.Узел читает файлы асинхронно, он будет читать все файлы одновременно.Итак, вы, вероятно, читаете предел 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()
2 голосов
/ 15 августа 2018

Я не уверен, поможет ли это кому-нибудь, я начал работать над большим проектом с большим количеством зависимостей, который выдал мне ту же ошибку.Мой коллега предложил мне установить watchman, используя brew, и это решило эту проблему для меня.

brew update
brew install watchman

Редактировать 26 июня 2019 года: Github ссылка на сторожа

2 голосов
/ 03 декабря 2012

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

Это решение просто останавливает любые вызовы fs.readFile или fs.writeFile, чтобы в любой момент времени в рейсе было не более установленного номера.

// Queuing reads and writes, so your nodejs script doesn't overwhelm system limits catastrophically
global.maxFilesInFlight = 100; // Set this value to some number safeish for your system
var origRead = fs.readFile;
var origWrite = fs.writeFile;

var activeCount = 0;
var pending = [];

var wrapCallback = function(cb){
    return function(){
        activeCount--;
        cb.apply(this,Array.prototype.slice.call(arguments));
        if (activeCount < global.maxFilesInFlight && pending.length){
            console.log("Processing Pending read/write");
            pending.shift()();
        }
    };
};
fs.readFile = function(){
    var args = Array.prototype.slice.call(arguments);
    if (activeCount < global.maxFilesInFlight){
        if (args[1] instanceof Function){
            args[1] = wrapCallback(args[1]);
        } else if (args[2] instanceof Function) {
            args[2] = wrapCallback(args[2]);
        }
        activeCount++;
        origRead.apply(fs,args);
    } else {
        console.log("Delaying read:",args[0]);
        pending.push(function(){
            fs.readFile.apply(fs,args);
        });
    }
};

fs.writeFile = function(){
    var args = Array.prototype.slice.call(arguments);
    if (activeCount < global.maxFilesInFlight){
        if (args[1] instanceof Function){
            args[1] = wrapCallback(args[1]);
        } else if (args[2] instanceof Function) {
            args[2] = wrapCallback(args[2]);
        }
        activeCount++;
        origWrite.apply(fs,args);
    } else {
        console.log("Delaying write:",args[0]);
        pending.push(function(){
            fs.writeFile.apply(fs,args);
        });
    }
};
1 голос
/ 31 июля 2018

Как и все мы, вы являетесь еще одной жертвой асинхронного ввода-вывода.При асинхронных вызовах, если вы зациклились на большом количестве файлов, Node.js начнет открывать файловый дескриптор для каждого файла для чтения, а затем будет ждать действия, пока вы его не закроете.

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

. Существует одно решение - создать очередь, чтобы избежать этого эффекта.

Спасибо людям, которые написали Async , для этого есть очень полезная функция.Существует метод с именем Async.queue , вы создаете новую очередь с ограничением, а затем добавляете имена файлов в очередь.

Примечание. Если вам нужно открыть много файлов, это будетХорошая идея хранить файлы, которые открыты в данный момент, и не открывать их бесконечно.

const fs = require('fs')
const async = require("async")

var q = async.queue(function(task, callback) {
    console.log(task.filename);
    fs.readFile(task.filename,"utf-8",function (err, data_read) {
            callback(err,task.filename,data_read);
        }
    );
}, 4);

var files = [1,2,3,4,5,6,7,8,9,10]

for (var file in files) {
    q.push({filename:file+".txt"}, function (err,filename,res) {
        console.log(filename + " read");
    });
}

Вы можете видеть, что каждый файл добавляется в очередь (имя файла console.log), но только когда текущийочередь под пределом, который вы установили ранее.

async.queue получает информацию о доступности очереди посредством обратного вызова, этот обратный вызов вызывается только тогда, когда файл данных читается и любое действие, которое вам нужно сделать, выполнено.(см. метод fileRead)

Таким образом, вы не можете быть перегружены дескриптором файлов.

> node ./queue.js
0.txt
    1.txt
2.txt
0.txt read
3.txt
3.txt read
4.txt
2.txt read
5.txt
4.txt read
6.txt
5.txt read
7.txt
    1.txt read (biggest file than other)
8.txt
6.txt read
9.txt
7.txt read
8.txt read
9.txt read
1 голос
/ 09 декабря 2015

Была такая же проблема при запуске команды nodemon , поэтому я уменьшил имя файлов, открытых в возвышенный текст , и ошибка исчезла.

1 голос
/ 20 ноября 2012

С волынкой нужно просто поменять

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

bagpipe.push(FS.readFile, filename, onRealRead))

Волынка поможет вам ограничить параллель. подробнее: https://github.com/JacksonTian/bagpipe

0 голосов
/ 10 мая 2016

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));
})
...