Узел и ошибка: 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
        }
    });
});

Я решил это, обновив сторож

       brew install watchman

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

Это решение просто останавливает любые вызовы 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);

Это записывает в файл "Бэтмен; Супермен".


Я установил сторожа, изменил лимит и т.д., и это не сработало в 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 слишком много открытых файлов

  • Перезагрузите компьютер
  • варить установить сторож

Это должно быть абсолютно решено.

Другие вопросы по тегам