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

Але яких проблем я повинен очікувати з вузлом? Це веб-сервер, перевірений виробництвом? Які ваші переживання?

Відповіді:


83

Бо коли витончені фс не спрацьовують ... або ви просто хочете зрозуміти, звідки йде витік. Дотримуйтесь цього процесу.

(наприклад, витончені-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.

Довідка на команду

Використовуйте цей синтаксис, щоб визначити, скільки відкритих ручок відкрив процес ...

Щоб отримати кількість відкритих файлів за певний під

Я використовував цю команду для перевірки кількості файлів, які були відкриті після різних подій у моєму додатку.

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
  • файл: /etc/security/limits.conf (додати до кінця або відредагувати, якщо він уже існує)

    root soft  nofile 40000
    root hard  nofile 100000
  • перезавантажте свої nodejs та вихід / вхід із ssh.

  • це може не працювати для старих NodeJS, вам потрібно буде перезапустити сервер
  • використовувати замість того, якщо ваш вузол працює з різними uid.

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, дуже просто. Не чекайте багато розуму від самого Вузла. Натомість, розраховуйте знайти розумні пакунки, опубліковані в npm.
Мірн Стол

5
Це добре, якщо це ваш власний код, але багато npm-модулів не використовують це.
UpTheCreek

1
Цей модуль вирішив усі мої проблеми! Я погоджуюся з тим, що вузол все ще залишається трохи неочищеним, але головним чином тому, що насправді важко зрозуміти, що не так у випадку з такою малою документацією та прийнятими правильними рішеннями відомих проблем.
sidonaldson

як це ти npm ? як я комбіную це в коді замість звичайного fs?
Авірам Нетанел

11

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

brew update
brew install watchman

Редагувати 26 червня 2019 року: посилання Github на сторожа


Це мені щонайменше допомогло. У реальному проекті bundler може або відкрити файли на власному рівні, або (якщо він встановлений) використовувати сторожового, щоб зробити це так, як приємніше для операційної системи. Тож це може бути великою допомогою - це зафіксовано навіть у реальному швидкому старті 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 почне відкривати дескриптор файлів для кожного файлу для читання, а потім чекатиме дій, поки ви не закриєте його.

Дескриптор файлу залишається відкритим, поки на вашому сервері не з’явиться ресурс для його читання. Навіть якщо ваші файли невеликі, і читання чи оновлення відбувається швидко, це потребує певного часу, але в той же час ваш цикл не зупиняється, щоб відкрити дескриптор нових файлів. Тож якщо у вас занадто багато файлів, скоро буде досягнуто обмеження, і ви отримаєте прекрасну 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 отримує інформацію про наявність черги за допомогою зворотного дзвінка, цей зворотний виклик викликається лише тоді, коли читається файл даних і досягаються будь-які дії, які потрібно зробити. (див. метод readRead)

Таким чином, ви не можете бути переповнені дескриптором файлів.

> 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

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

Ви повинні зробити репо для цього на github.
Нік

Це дуже добре працює, якщо витончена-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


Це все на китайській чи іншій азіатській мові. Чи є документація, написана англійською мовою?
Фатих Арслан

@FatihArslan англійська док. Доступна вже зараз.
користувач1837639

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

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, занадто багато відкритих файлів" зникло.

У мене виникли різноманітні проблеми на різних ОС 'з файловими системами, що обробляють вузли. Файлові системи, очевидно, не тривіальні.


0

У мене було це питання, і я вирішив його, запустивши, npm updateі воно спрацювало.

У деяких випадках може знадобитися видалити node_modules rm -rf node_modules/


0

Я встановив сторожа, змінив ліміт тощо, і це не працювало в Gulp.

Перезапуск iterm2 насправді допоміг.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.