nó e erro: EMFILE, muitos arquivos abertos

166

Durante alguns dias, procurei uma solução funcional para um erro

Error: EMFILE, too many open files

Parece que muitas pessoas têm o mesmo problema. A resposta usual envolve aumentar o número de descritores de arquivos. Então, eu tentei isso:

sysctl -w kern.maxfiles=20480,

O valor padrão é 10240. Isso é um pouco estranho aos meus olhos, porque o número de arquivos que estou manipulando no diretório está abaixo de 10240. Ainda mais estranho, ainda recebo o mesmo erro depois de aumentar o número de descritores de arquivos .

Segunda questão:

Após várias pesquisas, encontrei uma solução alternativa para o problema "muitos arquivos abertos":

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

Infelizmente ainda recebo o mesmo erro. O que está errado neste código?

Uma última pergunta (eu sou novo no javascript e no nó), estou desenvolvendo um aplicativo da web com muitas solicitações para cerca de 5.000 usuários diários. Tenho muitos anos de experiência em programação com outras linguagens como python e java. então originalmente pensei em desenvolver esta aplicação com django ou play framework. Descobri o nó e devo dizer que a ideia de modelo de E / S sem bloqueio é realmente agradável, sedutora e, acima de tudo, muito rápida!

Mas que tipo de problemas devo esperar com o nó? É um servidor Web comprovado em produção? Quais são as suas experiências?

xaverras
fonte

Respostas:

83

Para quando graceful-fs não funcionar ... ou você apenas quiser entender de onde vem o vazamento. Siga este processo.

(por exemplo, graceful-fs não vai consertar seu vagão se o problema for com soquetes.)

Artigo do meu blog: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

Como isolar

Este comando produzirá o número de identificadores abertos para processos 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)
....

Observe o: 1023u (última linha) - esse é o 1024º identificador de arquivo, que é o máximo padrão.

Agora, olhe para a última coluna. Isso indica qual recurso está aberto. Você provavelmente verá várias linhas com o mesmo nome de recurso. Felizmente, isso agora diz onde procurar no seu código o vazamento.

Se você não conhece vários processos de nós, primeiro procure o processo com o número 12211. Isso informará o processo.

No meu caso acima, notei que havia vários endereços IP muito semelhantes. Eles estavam todos. 54.236.3.### Ao fazer pesquisas de endereços IP, foi possível determinar no meu caso que estava relacionado ao pubnub.

Referência de Comandos

Use esta sintaxe para determinar quantas alças abertas um processo abriu ...

Para obter uma contagem de arquivos abertos para um determinado pid

Eu usei esse comando para testar o número de arquivos que foram abertos depois de realizar vários eventos no meu aplicativo.

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

Qual é o seu limite de processo?

ulimit -a

A linha que você deseja terá a seguinte aparência:

open files                      (-n) 1024

Altere permanentemente o limite:

  • testado no Ubuntu 14.04, nodejs v. 7.9

Caso pretenda abrir muitas conexões (websockets é um bom exemplo), você pode aumentar permanentemente o limite:

  • file: /etc/pam.d/common-session (adicione ao final)

    session required pam_limits.so
  • file: /etc/security/limits.conf (adicione ao final ou edite, se já existir)

    root soft  nofile 40000
    root hard  nofile 100000
  • reinicie o seu nodejs e efetue logout / login do ssh.

  • isso pode não funcionar para o NodeJS antigo, você precisará reiniciar o servidor
  • use em vez de se seu nó for executado com uid diferente.
blak3r
fonte
1
Como você pode alterar o limite de arquivos abertos?
Om3ga
13
ulimit -n 2048 para permitir a abertura de arquivos 2048
Gaël Barbin 11/11
1
Essa é a resposta mais descritiva e correta. Obrigado!
Kostanos
Eu tenho números raros. lsof -i -n -P | grep "12843" | wc -l== 4085 mas ulimit -a | grep "open files"== (-n) 1024 alguma pista de como eu poderia ter mais arquivos abertos que o limite máximo?
Kostanos
1
Como o blog de @ blak3r parece estar inativo, aqui está um link para seu artigo sobre a máquina de wayback. web.archive.org/web/20140508165434/http://… Super útil e uma ótima leitura!
James
72

Usar o graceful-fsmódulo de Isaac Schlueter (mantenedor do node.js.) é provavelmente a solução mais apropriada. Faz recuo incremental se EMFILE for encontrado. Ele pode ser usado como um substituto para o fsmódulo embutido .

Myrne Stol
fonte
2
Me salvou, por que esse não é o nó padrão? Por que preciso instalar algum plug-in de terceiros para resolver o problema?
Anthony Webb
7
Eu acho que, de um modo geral, o Node tenta expor o máximo possível ao usuário. Isso oferece a todos (e não apenas os desenvolvedores principais do Node) a oportunidade de resolver quaisquer problemas decorrentes do uso dessa interface relativamente bruta. Ao mesmo tempo, é realmente fácil publicar soluções e fazer o download das publicadas por outras pessoas através do npm. Não espere muita inteligência do próprio Node. Em vez disso, espere encontrar os smarts nos pacotes publicados no npm.
Myrne Stol
5
Tudo bem se for seu próprio código, mas muitos módulos npm não usam isso.
UpTheCreek
1
Este módulo resolveu todos os meus problemas! Concordo que o nó ainda parece um pouco cru, mas principalmente porque é realmente difícil entender o que está acontecendo de errado com tão pouca documentação e aceitar soluções corretas para problemas conhecidos.
Sidneyson
como você npm isso? como combinar isso no meu código em vez do fs regular?
Aviram Netanel
11

Não tenho certeza se isso vai ajudar alguém, comecei a trabalhar em um grande projeto com muitas dependências que me causaram o mesmo erro. Meu colega me sugeriu a instalação watchmanusando o brew e isso resolveu esse problema para mim.

brew update
brew install watchman

Editar em 26 de junho de 2019: link do Github para vigia

bh4r4th
fonte
Isso me ajudou pelo menos. Em um projeto nativo de reação, o bundler pode abrir os arquivos nativamente ou (se estiver instalado) usar o watchman para fazê-lo de uma maneira mais agradável para o sistema operacional. Portanto, pode ser uma grande ajuda - está documentada no início rápido da CLI nativa a reagir para o macOS: facebook.github.io/react-native/docs/getting-started.html - saúde!
Mike Hardy
7

Encontrei este problema hoje e, não encontrando boas soluções, criei um módulo para resolvê-lo. Fui inspirado pelo snippet do @ fbartho, mas queria evitar a substituição do módulo fs.

O módulo que escrevi é Filequeue e você o usa como o 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
        }
    });
});
Trey Griffith
fonte
7

Você está lendo muitos arquivos. O nó lê os arquivos de forma assíncrona, estará lendo todos os arquivos de uma só vez. Então você provavelmente está lendo o limite 10240.

Veja se isso funciona:

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()
Tim P.
fonte
6

Como todos nós, você é outra vítima de E / S assíncrona. Com chamadas assíncronas, se você fizer um loop em muitos arquivos, o Node.js começará a abrir um descritor de arquivo para cada arquivo para ler e aguardará a ação até você fechá-lo.

O descritor de arquivo permanece aberto até que o recurso esteja disponível no seu servidor para lê-lo. Mesmo que seus arquivos sejam pequenos e a leitura ou atualização seja rápida, isso leva algum tempo, mas, ao mesmo tempo, seu loop não para para abrir um novo descritor de arquivos. Portanto, se você tiver muitos arquivos, o limite será atingido em breve e você obterá um belo EMFILE .

Existe uma solução, criando uma fila para evitar esse efeito.

Graças às pessoas que escreveram Async , há uma função muito útil para isso. Existe um método chamado Async.queue , você cria uma nova fila com um limite e adiciona nomes de arquivos à fila.

Nota: Se você precisar abrir muitos arquivos, seria uma boa ideia armazenar quais arquivos estão abertos no momento e não reabri-los infinitamente.

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

Você pode ver que cada arquivo é adicionado à fila (nome do arquivo console.log), mas apenas quando a fila atual está abaixo do limite definido anteriormente.

async.queue obtém informações sobre a disponibilidade da fila por meio de um retorno de chamada, esse retorno de chamada é chamado apenas quando o arquivo de dados é lido e qualquer ação que você precisa executar é alcançada. (consulte o método fileRead)

Portanto, você não pode ficar impressionado com o descritor de arquivos.

> 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
fonte
3

Acabei de escrever um pequeno trecho de código para resolver esse problema, todas as outras soluções parecem muito pesadas e exigem que você altere a estrutura do programa.

Essa solução apenas interrompe todas as chamadas fs.readFile ou fs.writeFile para que não haja mais do que um número definido em andamento a qualquer momento.

// 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
fonte
Você deve fazer um repositório para isso no github.
Nick
Isso funciona muito bem se o graceful-fs não estiver funcionando para você.
CeeKay
3

Eu fiz todas as coisas acima mencionadas para o mesmo problema, mas nada funcionou. Eu tentei abaixo funcionou 100%. Alterações simples na configuração.

Limite definido da opção 1 (não funcionará na maioria das vezes)

user@ubuntu:~$ ulimit -n 65535

verifique o limite disponível

user@ubuntu:~$ ulimit -n
1024

Opção 2 Para aumentar o limite disponível para dizer 65535

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

adicione a seguinte linha a ela

fs.file-max = 65535

execute isso para atualizar com a nova configuração

user@ubuntu:~$ sudo sysctl -p

edite o seguinte arquivo

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

adicione as seguintes linhas a ele

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

edite o seguinte arquivo

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

adicione esta linha a ela

session required pam_limits.so

logout e login e tente o seguinte comando

user@ubuntu:~$ ulimit -n
65535

Opção 3 Basta adicionar a linha abaixo

DefaultLimitNOFILE=65535

para /etc/systemd/system.conf e /etc/systemd/user.conf

Rohit Parte
fonte
a opção 2 é bastante longa e esperava que a opção 3 funcionasse, mas não é para o meu ubuntu 18
eugene
1

Com gaita de foles, você só precisa mudar

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

A gaita de fole ajuda a limitar o paralelo. mais detalhes: https://github.com/JacksonTian/bagpipe

user1837639
fonte
É tudo em chinês ou outro idioma asiático. Existe alguma documentação escrita em inglês?
Fatih Arslan
O documento @FatihArslan English já está disponível.
user1837639
1

Tive o mesmo problema ao executar o comando nodemon, então reduzi o nome dos arquivos abertos em texto sublime e o erro desapareceu.

Buhiire Keneth
fonte
Eu também estava recebendo EMFILEerros e, por tentativa e erro, notei que o fechamento de algumas janelas Sublime resolveu o problema. Eu ainda não sei o porquê. Tentei adicionar ulimit -n 2560ao meu .bash_profile, mas isso não resolveu o problema. Isso indica a necessidade de mudar para Atom ?
O Qodesmith
1

Com base na resposta do @ blak3r, aqui está um pouco de abreviação que eu uso caso ajude outros diagnósticos:

Se você estiver tentando depurar um script Node.js que está ficando sem descritores de arquivo, aqui está uma linha para fornecer a saída lsofusada pelo processo do nó em questão:

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

Isso será executado de forma síncrona lsof filtrada pelo processo atual do Node.js. em execução e retornará os resultados via buffer.

Em seguida, use console.log(openFiles.toString())para converter o buffer em uma sequência e registrar os resultados.

James
fonte
0

esperar é uma solução geral para limitar execuções simultâneas de qualquer função que retorne promessas.

No seu caso, o código pode ser algo como:

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
fonte
0

Para usuários do nodemon : Basta usar o --ignore sinalizador para resolver o problema.

Exemplo:

nodemon app.js --ignore node_modules/ --ignore data/
Serdar Değirmenci
fonte
0

Use o mais recente fs-extra.

Eu tive esse problema Ubuntu(16 e 18) com bastante espaço para arquivos / descritores de soquete (conte com lsof |wc -l). fs-extraVersão usada 8.1.0. Após a atualização 9.0.0do "Erro: EMFILE, muitos arquivos abertos" desapareceram.

Eu experimentei diversos problemas em diversos sistemas operacionais com sistemas de arquivos para manipulação de nós. Os sistemas de arquivos obviamente não são triviais.

dr0i
fonte
0

Eu tive esse problema e o resolvi executando npm update e funcionou.

Em alguns casos, pode ser necessário remover o node_modules rm -rf node_modules/

Adnane Lamghari
fonte
0

Eu instalei o watchman, alterei o limite etc. e não funcionou no Gulp.

Reiniciar o iterm2 realmente ajudou.

Runnick
fonte