узел и ошибка: EMFILE, слишком много открытых файлов

166

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

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. Затем я открыл узел и должен сказать, что идея неблокирующей модели ввода / вывода действительно хороша, соблазнительна и, скорее всего, очень быстра!

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

xaverras
источник

Ответы:

83

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

(Например, 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

В случае, если вы ожидаете открыть много соединений (хороший пример - веб-сокеты), вы можете постоянно увеличивать лимит:

  • файл: /etc/pam.d/common-session (добавить в конец)

    session required pam_limits.so
  • file: /etc/security/limits.conf (добавить в конец или отредактировать, если он уже существует)

    root soft  nofile 40000
    root hard  nofile 100000
  • перезапустите ваш nodejs и выйдите / войдите из ssh.

  • это может не работать для старых NodeJS, вам нужно перезагрузить сервер
  • используйте вместо, если ваш узел работает с другим UID.
blak3r
источник
1
Как вы можете изменить лимит открытых файлов?
Om3ga
13
ulimit -n 2048, чтобы разрешить открытие 2048 файлов
Gaël Barbin
1
Это самый описательный и правильный ответ. Спасибо!
Костанос
У меня есть редкие номера. lsof -i -n -P | grep "12843" | wc -l== 4085 но ulimit -a | grep "open files"== (-n) 1024 какая-нибудь подсказка, как я могу иметь больше открытых файлов, чем максимальное ограничение?
Костанос
1
Поскольку блог @ blak3r не работает, вот ссылка на его статью о машине обратного хода. web.archive.org/web/20140508165434/http://… Очень полезно и действительно отличное чтение!
Джеймс
72

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

Мирн Стол
источник
2
Спас меня, почему это не узел по умолчанию? Почему мне нужно установить какой-нибудь сторонний плагин для решения проблемы?
Энтони Уэбб
7
Я думаю, что, вообще говоря, Node пытается раскрыть пользователю как можно больше. Это дает всем (а не только разработчикам ядра Node) возможность решать любые проблемы, возникающие при использовании этого относительно сырого интерфейса. В то же время очень легко публиковать решения и загружать решения, опубликованные другими пользователями, через npm. Не ожидайте много умов от самого Node. Вместо этого ожидайте найти смарты в пакетах, опубликованных на npm.
Мирн Стол
5
Это хорошо, если это ваш собственный код, но многие модули npm не используют его.
UpTheCreek
1
Этот модуль решил все мои проблемы! Я согласен с тем, что узел выглядит немного сырым, но в основном потому, что действительно трудно понять, что происходит с таким небольшим количеством документации и приемлемыми правильными решениями известных проблем.
Сидональдсон
как вы НПМ это? как мне объединить это в моем коде вместо обычного фс?
Авирам Нетанел
11

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

brew update
brew install watchman

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

bh4r4th
источник
Это помогло мне по крайней мере. В реактивном проекте сборщик может либо открывать файлы по своему усмотрению, либо (если он установлен) использовать сторож, чтобы сделать это способом, более приятным для операционной системы. Так что это может быть очень полезно - это задокументировано в кратком обзоре CLI для MacOS, даже если он реагирует на это: facebook.github.io/react-native/docs/getting-started.html - ура!
Майк Харди
7

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

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

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

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

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

Спасибо людям, которые написали 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
Plaute
источник
3

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

Это решение просто останавливает любые вызовы 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);
        });
    }
};
fbartho
источник
Вы должны сделать репо для этого на GitHub.
Ник
Это работает очень хорошо, если Graceful-FS не работает для вас.
Ceekay
3

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

Вариант 1 установить лимит (он не будет работать большую часть времени)

user@ubuntu:~$ ulimit -n 65535

проверить доступный лимит

user@ubuntu:~$ ulimit -n
1024

Вариант 2 Чтобы увеличить доступный лимит, скажем, 65535

user@ubuntu:~$ sudo nano /etc/sysctl.conf

добавить следующую строку к нему

fs.file-max = 65535

запустите это, чтобы обновить новую конфигурацию

user@ubuntu:~$ sudo sysctl -p

отредактируйте следующий файл

user@ubuntu:~$ sudo vim /etc/security/limits.conf

добавить следующие строки к нему

root soft     nproc          65535    
root hard     nproc          65535   
root soft     nofile         65535   
root hard     nofile         65535

отредактируйте следующий файл

user@ubuntu:~$ sudo vim /etc/pam.d/common-session

добавить эту строку к нему

session required pam_limits.so

Выйдите из системы и войдите в систему и попробуйте следующую команду

user@ubuntu:~$ ulimit -n
65535

Вариант 3 Просто добавьте строку ниже

DefaultLimitNOFILE=65535

в /etc/systemd/system.conf и /etc/systemd/user.conf

Рохит Парте
источник
Вариант 2 довольно длинный, и надеюсь, что вариант 3 сработает, но это не для моего Ubuntu 18
Евгений
1

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

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

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

user1837639
источник
Это все на китайском или другом азиатском языке. Есть ли документация на английском языке?
Фатих Арслан
@FatihArslan Английский документ доступен уже сейчас.
user1837639
1

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

Бухиер Кенет
источник
Я тоже получал EMFILEошибки и методом проб и ошибок заметил, что закрытие некоторых Sublime окон решило проблему. Я до сих пор не знаю почему. Я попытался добавить ulimit -n 2560в свой .bash_profile, но это не решило проблему. Означает ли это, что вместо этого нужно перейти на Atom ?
Кодесмит
1

Основываясь на ответе @ blak3r, вот несколько сокращений, которые я использую на случай, если это поможет другим диагностировать:

Если вы пытаетесь отладить скрипт Node.js, на котором заканчиваются файловые дескрипторы, вот строка, чтобы дать вам вывод, lsofиспользованный рассматриваемым процессом узла:

openFiles = child_process.execSync(`lsof -p ${process.pid}`);

Это будет работать синхронно lsof фильтром текущего запущенного процесса Node.js и возвращать результаты через буфер.

Затем используйте console.log(openFiles.toString())для преобразования буфера в строку и записи результатов.

Джеймс
источник
0

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));
})
jjrv
источник
0

Для пользователей nodemon : просто используйте флаг --ignore для решения проблемы.

Пример:

nodemon app.js --ignore node_modules/ --ignore data/
Сердар Дежирменци
источник
0

Используйте последние fs-extra.

У меня была такая проблема на Ubuntu(16 и 18) с большим количеством пространства файлов / сокетов-дескрипторов (считайте с lsof |wc -l). Используемая fs-extraверсия 8.1.0. После обновления 9.0.0«Ошибка: EMFILE, слишком много открытых файлов» исчезло.

Я сталкивался с различными проблемами в разных ОС с файловой системой обработки узлов. Файловые системы явно не тривиальны.

dr0i
источник
0

У меня была эта проблема, и я решил ее, запустив, npm updateи это сработало.

В некоторых случаях вам может понадобиться удалить node_modules rm -rf node_modules/

Аднане Ламгари
источник
0

Я установил сторож, изменил лимит и т. Д., И это не сработало в Gulp

Перезапуск iterm2 действительно помог, хотя.

Runnick
источник