Узел и ошибка: EMFILE, слишком много открытых файлов
В течение нескольких дней я искал рабочее решение ошибки
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. Затем я обнаружил узел и должен сказать, что идея неблокирующей модели ввода / вывода действительно хороша, соблазнительна и, скорее всего, очень быстра!
Но каких проблем мне ожидать с узлом? Это проверенный производственный веб-сервер? Каковы ваши переживания?
26 ответов
Потому что когда 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.
Справочник по командам
Используйте этот синтаксис, чтобы определить, сколько открытых дескрипторов есть у процесса...
Чтобы получить количество открытых файлов для определенного 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.
С использованием graceful-fs
Модуль Исаака Шлютера (сопровождающий node.js), вероятно, является наиболее подходящим решением. Это делает постепенный откат, если встречается EMFILE. Может использоваться в качестве замены для встроенного fs
модуль.
Я не уверен, поможет ли это кому-либо, я начал работать над большим проектом с большим количеством зависимостей, который выдал мне ту же ошибку. Мой коллега предложил мне установить watchman
используя Bre w, и это решило эту проблему для меня.
brew update
brew install watchman
Я сделал все вышеупомянутые вещи для той же проблемы, но ничего не помогло. Я пробовал ниже работать на 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
Как и все мы, вы являетесь еще одной жертвой асинхронного ввода-вывода. При асинхронных вызовах, если вы зациклились на большом количестве файлов, 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
Вы читаете слишком много файлов. Узел читает файлы асинхронно, он будет читать все файлы одновременно. Итак, вы, вероятно, читаете предел 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()
Я столкнулся с этой проблемой сегодня, и, не найдя хороших решений, я создал модуль для ее решения. Я был вдохновлен фрагментом @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
}
});
});
Я только что закончил писать небольшой фрагмент кода, чтобы решить эту проблему самостоятельно, все другие решения кажутся слишком тяжелыми и требуют изменения структуры вашей программы.
Это решение просто останавливает любые вызовы 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);
});
}
};
У меня была такая же проблема при запуске команды nodemon, поэтому я уменьшил имя файлов, открытых в возвышенном тексте, и ошибка исчезла.
С волынкой нужно просто поменять
FS.readFile(filename, onRealRead);
=>
var bagpipe = new Bagpipe(10);
bagpipe.push(FS.readFile, filename, onRealRead))
Волынка поможет вам ограничить параллель. более подробная информация: https://github.com/JacksonTian/bagpipe
Для пользователей nodemon: просто используйте флаг --ignore, чтобы решить проблему.
Пример:
nodemon app.js --ignore node_modules/ --ignore data/
Основываясь на ответе @blak3r, вот небольшое сокращение, которое я использую на случай, если он поможет другим диагностировать:
Если вы пытаетесь отладить сценарий Node.js, у которого заканчиваются файловые дескрипторы, вот строка, которая даст вам вывод lsof
используется рассматриваемым процессом узла:
openFiles = child_process.execSync(`lsof -p ${process.pid}`);
Это будет синхронно работать lsof
фильтруется текущим запущенным процессом Node.js и возвращает результаты через буфер.
Затем используйте console.log(openFiles.toString())
для преобразования буфера в строку и регистрации результатов.
Есть еще одна возможность, которая до сих пор не рассматривалась и не обсуждалась ни в одном из ответов: циклы символических ссылок.
Наблюдатель за рекурсивной файловой системой Node, похоже, не обнаруживает и не обрабатывает циклы символических ссылок. Таким образом, вы можете легко вызвать эту ошибку с произвольно высоким
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, too many open files
ошибка.
Помимо прочего, это может произойти в
node_modules
где есть отношения сдерживания:
parent/
package.json
child/
package.json
вот как я столкнулся с этим в проекте, который пытался построить.
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));
})
Используйте последнюю fs-extra
.
У меня была эта проблема на Ubuntu
(16 и 18) с большим количеством места для дескрипторов файлов / сокетов (считайте с lsof |wc -l
). Используемыйfs-extra
версия 8.1.0
. После обновления до9.0.0
"Ошибка: EMFILE, слишком много открытых файлов" исчезло.
Я столкнулся с различными проблемами в различных ОС с файловыми системами обработки узлов. Файловые системы явно нетривиальны.
Вот мои два цента: учитывая, что файл CSV - это просто строки текста, я передал данные (строки), чтобы избежать этой проблемы.
Самое простое решение для меня, которое сработало в моем случае.
Его можно использовать с изящной fs или стандартной fs. Только учтите, что при создании в файле не будет заголовков.
// import graceful-fs or normal fs
const fs = require("graceful-fs"); // or use: const fs = require("fs")
// Create output file and set it up to receive streamed data
// Flag is to say "append" so that data can be recursively added to the same file
let fakeCSV = fs.createWriteStream("./output/document.csv", {
flags: "a",
});
и данные, которые необходимо передать в файл, который я сделал вот так
// create custom streamer that can be invoked when needed
const customStreamer = (dataToWrite) => {
fakeCSV.write(dataToWrite + "\n");
};
Обратите внимание, что dataToWrite - это просто строка с настраиваемым разделителем, например ";" или же ",". т.е.
const dataToWrite = "batman" + ";" + "superman"
customStreamer(dataToWrite);
Это записывает в файл "Бэтмен; Супермен".
- Обратите внимание, что в этом примере нет обнаружения ошибок или чего-либо еще.
- Документы: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
Я установил сторожа, изменил лимит и т.д., и это не сработало в Gulp.
Однако перезапуск iterm2 действительно помог.
Это, вероятно, решит вашу проблему, если вы изо всех сил пытаетесь развернуть решение React, которое было создано с помощью шаблона Visual Studio (и имеет web.config). В Azure Release Pipelines при выборе шаблона используйте:
Развертывание службы приложений Azure
Вместо:
Развертывание приложения Node.js в службе приложений Azure
У меня это сработало!
Обратите внимание, что вам не обязательно усложнять эту проблему, повторная попытка работает нормально.
import { promises as fs } from "fs";
const filepaths = [];
const errors = [];
function process_file(content: string) {
// logic here
}
await Promise.all(
filepaths.map(function read_each(filepath) {
return fs
.readFile(filepath, "utf8")
.then(process_file)
.catch(function (error) {
if (error.code === "EMFILE") return read_each(filepath);
else errors.push({ file: filepath, error });
});
}),
);
Для тех, кто все еще может искать решения, использование async-await отлично сработало:
fs.readdir(<directory path></directory>, async (err, filenames) => {
if (err) {
console.log(err);
}
try {
for (let filename of filenames) {
const fileContent = await new Promise((resolve, reject) => {
fs.readFile(<dirctory path + filename>, 'utf-8', (err, content) => {
if (err) {
reject(err);
}
resolve(content);
});
});
... // do things with fileContent
}
} catch (err) {
console.log(err);
}
});
То, что вы сделали, почти правильно:
sysctl -w kern.maxfiles=20480
В моей macOS значением по умолчанию является 491520, где значение, которое вы установили, на самом деле меньше, чем значение по умолчанию в моей системе, я просто установил его на 999999, и оно работало отлично. Больше этой ошибки нет.
Изменить : я забыл упомянуть о перезагрузке после этого.
Надеюсь это поможет.
В Windows кажется, что нет
ulimit
Команда для увеличения количества открытых файлов. В
graceful-fs
, он поддерживает очередь для выполнения операций ввода-вывода, например: чтение/запись файла.
Однако,
fs.readFile, fs.writeFile
основаны на
fs.open
, поэтому вам нужно будет открывать/закрывать файлы вручную, чтобы решить эту ошибку.
import fs from 'fs/promises';
const fd = await fs.open('path-to-file', 'r');
await fd.readFile('utf-8'); // <== read through file handle
await fd.close(); // <== manually close it
сначала обновите свою версию выставки, используя
expo update
а затем запустить
yarn / npm install
. Это решило проблему для меня!
У меня была эта проблема, и я решил ее, запустив npm update
и это сработало.
В некоторых случаях вам может потребоваться удалить node_modules rm -rf node_modules/
Это может произойти после изменения emfile версии узла ERR слишком много открытых файлов
- Перезагрузите компьютер
- варить установить сторож
Это должно быть абсолютно решено.