nodo ed errore: EMFILE, troppi file aperti


166

Per alcuni giorni ho cercato una soluzione funzionante a un errore

Error: EMFILE, too many open files

Sembra che molte persone abbiano lo stesso problema. La solita risposta comporta l'aumento del numero di descrittori di file. Quindi, ho provato questo:

sysctl -w kern.maxfiles=20480,

Il valore predefinito è 10240. Questo è un po 'strano ai miei occhi, perché il numero di file che gestisco nella directory è inferiore a 10240. Ancora più strano, ricevo ancora lo stesso errore dopo aver aumentato il numero di descrittori di file .

Seconda domanda:

Dopo una serie di ricerche ho trovato un problema per il problema "troppi file aperti":

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

Purtroppo continuo a ricevere lo stesso errore. Cosa c'è di sbagliato in questo codice?

Un'ultima domanda (sono nuovo di JavaScript e nodo), sto sviluppando un'applicazione web con molte richieste per circa 5000 utenti giornalieri. Ho molti anni di esperienza nella programmazione con altri linguaggi come Python e Java. così originariamente ho pensato di sviluppare questa applicazione con django o play framework. Poi ho scoperto il nodo e devo dire che l'idea del modello I / O non bloccante è davvero carina, seducente e soprattutto molto veloce!

Ma che tipo di problemi dovrei aspettarmi con il nodo? È un web server collaudato in produzione? Quali sono le tue esperienze?

Risposte:


83

Perché quando graceful-fs non funziona ... o vuoi solo capire da dove proviene la perdita. Segui questo processo.

(ad esempio graceful-fs non risolverà il tuo carro se il tuo problema è con socket.)

Dal mio articolo sul blog: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

Come isolare

Questo comando genererà il numero di handle aperti per i processi 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)
....

Nota: 1023u (ultima riga) - è l'handle del file 1024 che è il massimo predefinito.

Ora, guarda l'ultima colonna. Ciò indica quale risorsa è aperta. Probabilmente vedrai un numero di righe tutte con lo stesso nome di risorsa. Speriamo che ora ti dica dove cercare la perdita nel tuo codice.

Se non conosci processi a più nodi, prima cerca quale processo ha eseguito il pid 12211. Questo ti dirà il processo.

Nel mio caso sopra, ho notato che c'erano un sacco di indirizzi IP molto simili. Erano tutti 54.236.3.### facendo ricerche di indirizzi IP, è stato in grado di determinare nel mio caso che era relativo a pubnub.

Riferimenti sui comandi

Utilizzare questa sintassi per determinare il numero di handle aperti aperti da un processo ...

Per ottenere un conteggio dei file aperti per un determinato pid

Ho usato questo comando per testare il numero di file aperti dopo aver fatto vari eventi nella mia app.

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 è il tuo limite di processo?

ulimit -a

La linea desiderata sarà simile a questa:

open files                      (-n) 1024

Modifica permanente del limite:

  • testato su Ubuntu 14.04, nodejs v. 7.9

Nel caso in cui ti aspetti di aprire molte connessioni (websocket è un buon esempio), puoi aumentare permanentemente il limite:

  • file: /etc/pam.d/common-session (aggiungi alla fine)

    session required pam_limits.so
  • file: /etc/security/limits.conf (aggiungi alla fine o modifica se esiste già)

    root soft  nofile 40000
    root hard  nofile 100000
  • riavvia i tuoi nodejs e disconnetti / accedi da ssh.

  • questo potrebbe non funzionare con NodeJS precedente, dovrai riavviare il server
  • usa invece di se il tuo nodo viene eseguito con uid diverso.

1
Come puoi modificare il limite dei file aperti?
Om3ga,

13
ulimit -n 2048 per consentire l'apertura di 2048 file
Gaël Barbin,

1
Questa è la risposta più descrittiva e corretta. Grazie!
Kostanos,

Ho numeri rari. lsof -i -n -P | grep "12843" | wc -l== 4085 ma ulimit -a | grep "open files"== (-n) 1024 qualche indizio su come avrei potuto avere più file aperti rispetto al limite massimo?
Kostanos,

1
Poiché il blog di @ blak3r sembra essere inattivo, ecco un link al suo articolo sulla macchina del ritorno. web.archive.org/web/20140508165434/http://… Super utile e una lettura davvero fantastica!
James,

72

L'uso del graceful-fsmodulo di Isaac Schlueter (maintainer node.js) è probabilmente la soluzione più appropriata. Esegue un back-off incrementale se viene rilevato EMFILE. Può essere utilizzato come sostituto drop-in per il fsmodulo integrato.


2
Mi ha salvato, perché questo non è il nodo predefinito? Perché devo installare alcuni plugin di terze parti per risolvere il problema?
Anthony Webb,

7
Penso che, in generale, Node cerchi di esporre il più possibile all'utente. Ciò offre a tutti (non solo agli sviluppatori core Node) l'opportunità di risolvere eventuali problemi derivanti dall'uso di questa interfaccia relativamente grezza. Allo stesso tempo, è davvero facile pubblicare soluzioni e scaricare quelle pubblicate da altri tramite npm. Non aspettarti molte intelligenze dal nodo stesso. Invece, aspettati di trovare gli smart nei pacchetti pubblicati su npm.
Myrne Stol,

5
Va bene se è il tuo codice, ma molti moduli npm non lo usano.
UpTheCreek,

1
Questo modulo ha risolto tutti i miei problemi! Sono d'accordo sul fatto che il nodo sembra essere ancora un po 'grezzo, ma principalmente perché è davvero difficile capire cosa non va con così poca documentazione e ha accettato le giuste soluzioni a problemi noti.
Sidonaldson,

come si fa a NPM esso? come posso combinare questo nel mio codice anziché nel normale fs?
Aviram Netanel,

11

Non sono sicuro che questo possa aiutare qualcuno, ho iniziato a lavorare su un grande progetto con molte dipendenze che mi hanno gettato lo stesso errore. Il mio collega mi ha suggerito di installare watchmanusando brew e questo ha risolto questo problema per me.

brew update
brew install watchman

Modifica il 26 giugno 2019: link Github a watchman


Questo mi ha aiutato almeno. In un progetto con reazione nativa, il bundler può aprire i file in modo nativo o (se installato) utilizzare watchman per farlo in un modo che sia più gradito al sistema operativo. Quindi può essere di grande aiuto - è documentato nella guida rapida della CLI di reazione nativa per macOS: facebook.github.io/react-native/docs/getting-started.html - evviva!
Mike Hardy,

7

Oggi ho riscontrato questo problema e, non trovando soluzioni valide, ho creato un modulo per risolverlo. Sono stato ispirato dallo snippet di @ fbartho, ma volevo evitare di sovrascrivere il modulo fs.

Il modulo che ho scritto è Filequeue e lo usi proprio come 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

Stai leggendo troppi file. Il nodo legge i file in modo asincrono, leggerà tutti i file contemporaneamente. Quindi probabilmente stai leggendo il limite 10240.

Vedi se funziona:

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

Come tutti noi, sei un'altra vittima dell'I / O asincrono. Con le chiamate asincrone, se esegui il ciclo attorno a molti file, Node.js inizierà ad aprire un descrittore di file per ciascun file da leggere, quindi attenderà l'azione fino alla chiusura.

Il descrittore di file rimane aperto fino a quando la risorsa non è disponibile sul server per leggerlo. Anche se i tuoi file sono piccoli e la lettura o l'aggiornamento è veloce, ci vuole del tempo, ma allo stesso tempo il tuo ciclo non si ferma per aprire un nuovo descrittore di file. Quindi se hai troppi file, il limite sarà presto raggiunto e otterrai un bellissimo EMFILE .

C'è una soluzione, creare una coda per evitare questo effetto.

Grazie alle persone che hanno scritto Async , c'è una funzione molto utile per questo. Esiste un metodo chiamato Async.queue , si crea una nuova coda con un limite e quindi si aggiungono nomi di file alla coda.

Nota: se devi aprire molti file, sarebbe una buona idea memorizzare quali file sono attualmente aperti e non riaprirli all'infinito.

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

Puoi vedere che ogni file viene aggiunto alla coda (nome file console.log), ma solo quando la coda corrente è al di sotto del limite impostato in precedenza.

async.queue ottiene informazioni sulla disponibilità della coda tramite un callback, questo callback viene chiamato solo quando viene letto il file di dati e viene raggiunta qualsiasi azione che si deve fare. (vedi metodo fileRead)

Quindi non puoi essere sopraffatto dal descrittore di file.

> 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

Ho appena finito di scrivere un piccolo frammento di codice per risolvere questo problema da solo, tutte le altre soluzioni sembrano troppo pesanti e richiedono di cambiare la struttura del programma.

Questa soluzione blocca qualsiasi chiamata fs.readFile o fs.writeFile in modo che non ci sia più di un numero impostato in volo in un dato 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);
        });
    }
};

Dovresti fare un repository per questo su Github.
Nick,

Funziona molto bene se graceful-fs non funziona per te.
Ceekay,

3

Ho fatto tutte le cose sopra menzionate per lo stesso problema, ma nulla ha funzionato. Ho provato di seguito ha funzionato al 100%. Modifiche alla configurazione semplice.

Opzione 1 imposta il limite (non funzionerà per la maggior parte del tempo)

user@ubuntu:~$ ulimit -n 65535

controlla il limite disponibile

user@ubuntu:~$ ulimit -n
1024

Opzione 2 Per aumentare il limite disponibile per dire 65535

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

aggiungere la seguente riga ad esso

fs.file-max = 65535

esegui questo per aggiornare con la nuova configurazione

user@ubuntu:~$ sudo sysctl -p

modifica il seguente file

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

aggiungi le seguenti righe

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

modifica il seguente file

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

aggiungere questa riga ad esso

session required pam_limits.so

disconnettersi e accedere e provare il comando seguente

user@ubuntu:~$ ulimit -n
65535

Opzione 3 Basta aggiungere sotto la riga in

DefaultLimitNOFILE=65535

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


l'opzione 2 è piuttosto lunga e speravo che l'opzione 3 funzionasse, ma non è per il mio ubuntu 18
eugene

1

Con la cornamusa basta cambiare

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

La cornamusa ti aiuta a limitare il parallelo. maggiori dettagli: https://github.com/JacksonTian/bagpipe


È tutto in cinese o altra lingua asiatica. C'è qualche documentazione scritta in inglese?
Fatih Arslan,

@FatihArslan English doc è ora disponibile.
user1837639

1

Ho avuto lo stesso problema durante l'esecuzione del comando nodemon, quindi ho ridotto il nome dei file aperti in testo sublime e l'errore è scomparso.


Anch'io stavo ricevendo EMFILEerrori e attraverso prove ed errori ho notato che la chiusura di alcune finestre Sublime ha risolto il problema. Ancora non so perché. Ho provato ad aggiungere ulimit -n 2560al mio .bash_profile, ma questo non ha risolto il problema. Indica invece la necessità di passare ad Atom ?
Il Qodesmith il

1

Sulla base della risposta di @ blak3r, ecco un po 'di stenografia che uso nel caso in cui aiuti altre diagnosi:

Se stai cercando di eseguire il debug di uno script Node.js che sta esaurendo i descrittori di file, ecco una riga che ti fornisce l'output di lsofusato dal processo del nodo in questione:

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

Questo verrà eseguito in modo sincrono lsoffiltrato dall'attuale processo Node.js in esecuzione e restituirà i risultati tramite buffer.

Quindi utilizzare console.log(openFiles.toString())per convertire il buffer in una stringa e registrare i risultati.


0

cwait è una soluzione generale per limitare le esecuzioni simultanee di qualsiasi funzione che restituisca promesse.

Nel tuo caso il codice potrebbe essere qualcosa del tipo:

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

Per utenti nodemon : basta usare il flag --ignore per risolvere il problema.

Esempio:

nodemon app.js --ignore node_modules/ --ignore data/

0

Usa l'ultimo fs-extra.

Ho avuto quel problema su Ubuntu(16 e 18) con un sacco di spazio per i descrittori di file / socket (conta con lsof |wc -l). fs-extraVersione usata 8.1.0. Dopo l'aggiornamento a 9.0.0"Errore: EMFILE, troppi file aperti" sono scomparsi.

Ho riscontrato diversi problemi su diversi sistemi operativi con i filesystem di gestione dei nodi. I filesystem non sono ovviamente banali.


0

Ho avuto questo problema e l'ho risolto correndo npm updatee ha funzionato.

In alcuni casi potrebbe essere necessario rimuovere node_modules rm -rf node_modules/


0

Ho installato Watchman, cambiando limite ecc. E non ha funzionato in Gulp.

Il riavvio di iterm2 in realtà ha aiutato però.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.