Knoten und Fehler: EMFILE, zu viele geöffnete Dateien


164

Seit einigen Tagen habe ich nach einer funktionierenden Lösung für einen Fehler gesucht

Error: EMFILE, too many open files

Es scheint, dass viele Menschen das gleiche Problem haben. Die übliche Antwort besteht darin, die Anzahl der Dateideskriptoren zu erhöhen. Also habe ich das versucht:

sysctl -w kern.maxfiles=20480,

Der Standardwert ist 10240. Dies ist in meinen Augen etwas seltsam, da die Anzahl der Dateien, die ich im Verzeichnis verarbeite, unter 10240 liegt. Noch seltsamer ist, dass ich immer noch den gleichen Fehler erhalte, nachdem ich die Anzahl der Dateideskriptoren erhöht habe .

Zweite Frage:

Nach einer Reihe von Suchen habe ich eine Lösung für das Problem "zu viele offene Dateien" gefunden:

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

Leider erhalte ich immer noch den gleichen Fehler. Was ist los mit diesem Code?

Eine letzte Frage (ich bin neu in Javascript und Node): Ich bin dabei, eine Webanwendung mit vielen Anfragen für etwa 5000 Benutzer pro Tag zu entwickeln. Ich habe langjährige Erfahrung in der Programmierung mit anderen Sprachen wie Python und Java. Ursprünglich dachte ich, diese Anwendung mit Django oder Play Framework zu entwickeln. Dann habe ich Node entdeckt und ich muss sagen, dass die Idee eines nicht blockierenden E / A-Modells wirklich nett, verführerisch und vor allem sehr schnell ist!

Aber welche Probleme sollte ich mit Node erwarten? Ist es ein produktionserprobter Webserver? Was sind deine Erfahrungen?

Antworten:


83

Für wenn anmutig-fs nicht funktioniert ... oder Sie einfach nur verstehen wollen, woher das Leck kommt. Folgen Sie diesem Prozess.

(zB wird Graceful-Fs Ihren Wagen nicht reparieren, wenn Sie Probleme mit Steckdosen haben.)

Aus meinem Blog-Artikel: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

So isolieren Sie

Dieser Befehl gibt die Anzahl der offenen Handles für NodeJS-Prozesse aus:

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

Beachten Sie die: 1023u (letzte Zeile) - das ist das 1024. Dateihandle, das das Standardmaximum ist.

Schauen Sie sich jetzt die letzte Spalte an. Das zeigt an, welche Ressource offen ist. Sie werden wahrscheinlich mehrere Zeilen mit demselben Ressourcennamen sehen. Hoffentlich sagt Ihnen das jetzt, wo Sie in Ihrem Code nach dem Leck suchen müssen.

Wenn Sie nicht mehrere Knotenprozesse kennen, suchen Sie zuerst, welcher Prozess die PID 12211 hat. Das zeigt Ihnen den Prozess.

In meinem obigen Fall habe ich festgestellt, dass es eine Reihe sehr ähnlicher IP-Adressen gibt. Sie waren alle 54.236.3.### Durch die Suche nach IP-Adressen konnte ich feststellen, dass es sich um einen Pubnub handelte.

Befehlsreferenz

Verwenden Sie diese Syntax, um zu bestimmen, wie viele offene Handles ein Prozess geöffnet hat ...

Um eine Anzahl geöffneter Dateien für eine bestimmte PID zu erhalten

Mit diesem Befehl habe ich die Anzahl der Dateien getestet, die nach verschiedenen Ereignissen in meiner App geöffnet wurden.

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

Was ist Ihr Prozesslimit?

ulimit -a

Die gewünschte Zeile sieht folgendermaßen aus:

open files                      (-n) 1024

Ändern Sie das Limit dauerhaft:

  • getestet unter Ubuntu 14.04, nodejs v. 7.9

Wenn Sie damit rechnen, viele Verbindungen zu öffnen (Websockets sind ein gutes Beispiel), können Sie das Limit dauerhaft erhöhen:

  • Datei: /etc/pam.d/common-session (am Ende hinzufügen)

    session required pam_limits.so
  • Datei: /etc/security/limits.conf (am Ende hinzufügen oder bearbeiten, falls bereits vorhanden)

    root soft  nofile 40000
    root hard  nofile 100000
  • Starten Sie Ihre NodeJS neu und melden Sie sich von SSH ab.

  • Dies funktioniert möglicherweise nicht für ältere NodeJS, die Sie benötigen, um den Server neu zu starten
  • Verwenden Sie anstelle von, wenn Ihr Knoten mit einer anderen UID ausgeführt wird.

1
Wie können Sie das Limit für geöffnete Dateien ändern?
Om3ga

13
ulimit -n 2048, um 2048 Dateien öffnen zu lassen
Gaël Barbin

1
Dies ist die aussagekräftigste und korrekteste Antwort. Danke dir!
Kostanos

Ich habe seltene Zahlen. lsof -i -n -P | grep "12843" | wc -l== 4085 aber ulimit -a | grep "open files"== (-n) 1024 Gibt es einen Hinweis darauf, wie ich mehr geöffnete Dateien als maximal haben könnte?
Kostanos

1
Da der Blog von @ blak3r nicht verfügbar zu sein scheint, finden Sie hier einen Link zu seinem Artikel über die Wayback-Maschine. web.archive.org/web/20140508165434/http://… Super hilfreich und eine wirklich gute Lektüre!
James

72

Die Verwendung des graceful-fsModuls von Isaac Schlueter (node.js Betreuer) ist wahrscheinlich die am besten geeignete Lösung. Es wird ein inkrementelles Backoff durchgeführt, wenn EMFILE auftritt. Es kann als Ersatz für das eingebaute fsModul verwendet werden.


2
Hat mich gerettet, warum ist dies nicht die Standardeinstellung des Knotens? Warum muss ich ein Plugin eines Drittanbieters installieren, um das Problem zu beheben?
Anthony Webb

7
Ich denke, im Allgemeinen versucht Node, dem Benutzer so viel wie möglich zugänglich zu machen. Dies gibt jedem (nicht nur Node-Kernentwicklern) die Möglichkeit, Probleme zu lösen, die sich aus der Verwendung dieser relativ rohen Schnittstelle ergeben. Gleichzeitig ist es wirklich einfach, Lösungen zu veröffentlichen und die von anderen veröffentlichten über npm herunterzuladen. Erwarten Sie von Node selbst nicht viel Smarts. Erwarten Sie stattdessen, die Smarts in Paketen zu finden, die auf npm veröffentlicht wurden.
Myrne Stol

5
Das ist in Ordnung, wenn es Ihr eigener Code ist, aber viele npm-Module verwenden diesen nicht.
UpTheCreek

1
Dieses Modul hat alle meine Probleme gelöst! Ich bin damit einverstanden, dass der Knoten immer noch ein wenig roh zu sein scheint, aber hauptsächlich, weil es wirklich schwer zu verstehen ist, was mit so wenig Dokumentation und akzeptierten richtigen Lösungen für bekannte Probleme falsch läuft.
Sidonaldson

wie Sie NPM es? Wie kombiniere ich dies in meinem Code anstelle der regulären fs?
Aviram Netanel

11

Ich bin mir nicht sicher, ob dies irgendjemandem helfen wird. Ich begann an einem großen Projekt mit vielen Abhängigkeiten zu arbeiten, das mir den gleichen Fehler verursachte. Mein Kollege schlug mir vor, watchmanmit Brew zu installieren , und das hat dieses Problem für mich behoben.

brew update
brew install watchman

Bearbeiten am 26. Juni 2019: Github Link zum Wächter


Das hat mir zumindest geholfen. Bei einem reaktionsnativen Projekt kann der Bundler die Dateien entweder nativ öffnen oder (falls installiert) Watchman verwenden, um dies auf eine Weise zu tun, die für das Betriebssystem besser ist. Es kann also eine große Hilfe sein - es ist sogar im reaktionsnativen CLI-Schnellstart für macOS dokumentiert: facebook.github.io/react-native/docs/getting-started.html - Prost!
Mike Hardy

7

Ich bin heute auf dieses Problem gestoßen, und da ich keine guten Lösungen dafür gefunden habe, habe ich ein Modul erstellt, um es anzugehen. Ich war von @ fbarthos Snippet inspiriert, wollte aber vermeiden, das fs-Modul zu überschreiben.

Das Modul, das ich geschrieben habe, ist Filequeue , und Sie verwenden es genau wie 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

Sie lesen zu viele Dateien. Der Knoten liest Dateien asynchron, er liest alle Dateien gleichzeitig. Sie lesen also wahrscheinlich das 10240-Limit.

Überprüfen Sie, ob dies funktioniert:

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

Wie wir alle sind Sie ein weiteres Opfer asynchroner E / A. Wenn Sie bei asynchronen Aufrufen viele Dateien durchlaufen, öffnet Node.js für jede zu lesende Datei einen Dateideskriptor und wartet dann auf eine Aktion, bis Sie sie schließen.

Der Dateideskriptor bleibt geöffnet, bis auf Ihrem Server eine Ressource zum Lesen verfügbar ist. Selbst wenn Ihre Dateien klein sind und das Lesen oder Aktualisieren schnell ist, dauert es einige Zeit, aber gleichzeitig hört Ihre Schleife nicht auf, den Deskriptor für neue Dateien zu öffnen. Wenn Sie also zu viele Dateien haben, ist das Limit bald erreicht und Sie erhalten eine schöne EMFILE .

Es gibt eine Lösung: Erstellen einer Warteschlange, um diesen Effekt zu vermeiden.

Dank der Leute, die Async geschrieben haben , gibt es dafür eine sehr nützliche Funktion. Es gibt eine Methode namens Async.queue . Sie erstellen eine neue Warteschlange mit einem Limit und fügen der Warteschlange dann Dateinamen hinzu.

Hinweis: Wenn Sie viele Dateien öffnen müssen, ist es eine gute Idee, zu speichern, welche Dateien gerade geöffnet sind, und sie nicht unendlich erneut zu öffnen.

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

Sie können sehen, dass jede Datei zur Warteschlange hinzugefügt wird (Dateiname console.log), jedoch nur, wenn die aktuelle Warteschlange unter dem zuvor festgelegten Grenzwert liegt.

async.queue Informationen über die Verfügbarkeit der Warteschlange über einen Rückruf abrufen. Dieser Rückruf wird nur aufgerufen, wenn die Datendatei gelesen wird und alle erforderlichen Aktionen ausgeführt werden. (siehe fileRead-Methode)

Sie können also nicht vom Dateideskriptor überwältigt werden.

> 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

Ich habe gerade ein kleines Stück Code geschrieben, um dieses Problem selbst zu lösen. Alle anderen Lösungen scheinen viel zu schwer zu sein und erfordern, dass Sie Ihre Programmstruktur ändern.

Diese Lösung blockiert nur alle fs.readFile- oder fs.writeFile-Aufrufe, sodass zu einem bestimmten Zeitpunkt nicht mehr als eine festgelegte Nummer im Flug ist.

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

Du solltest ein Repo dafür auf Github machen.
Nick

Dies funktioniert sehr gut, wenn Graceful-Fs für Sie nicht funktioniert.
Ceekay

3

Ich habe alle oben genannten Dinge für das gleiche Problem getan, aber nichts hat funktioniert. Ich habe unten versucht, es hat 100% funktioniert. Einfache Konfigurationsänderungen.

Option 1 festgelegtes Limit (funktioniert meistens nicht)

user@ubuntu:~$ ulimit -n 65535

Überprüfen Sie das verfügbare Limit

user@ubuntu:~$ ulimit -n
1024

Option 2 Um das verfügbare Limit auf 65535 zu erhöhen

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

Fügen Sie die folgende Zeile hinzu

fs.file-max = 65535

Führen Sie dies aus, um mit der neuen Konfiguration zu aktualisieren

user@ubuntu:~$ sudo sysctl -p

Bearbeiten Sie die folgende Datei

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

Fügen Sie die folgenden Zeilen hinzu

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

Bearbeiten Sie die folgende Datei

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

füge diese Zeile hinzu

session required pam_limits.so

Melden Sie sich ab und melden Sie sich an und versuchen Sie den folgenden Befehl

user@ubuntu:~$ ulimit -n
65535

Option 3 Fügen Sie einfach die folgende Zeile hinzu

DefaultLimitNOFILE=65535

zu /etc/systemd/system.conf und /etc/systemd/user.conf


2 Option ist ziemlich lang, und hoffte , dass die Option 3 Arbeit, aber es ist nicht für mein ubuntu 18
eugene

1

Mit Dudelsack brauchen Sie nur Wechselgeld

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

Der Dudelsack hilft Ihnen, die Parallele zu begrenzen. Weitere Details: https://github.com/JacksonTian/bagpipe


Es ist alles auf Chinesisch oder einer anderen asiatischen Sprache. Gibt es Unterlagen in englischer Sprache?
Fatih Arslan

@FatihArslan English doc ist ab sofort verfügbar.
user1837639

1

Hatte das gleiche Problem beim Ausführen des Befehls nodemon, so dass ich den Namen der in erhabenem Text geöffneten Dateien reduzierte und der Fehler verschwand.


Auch ich bekam EMFILEFehler und durch Versuch und Irrtum bemerkte ich, dass das Schließen einiger Sublime- Fenster das Problem löste. Ich weiß immer noch nicht warum. Ich habe versucht ulimit -n 2560, mein .bash_profile zu erweitern, aber das hat das Problem nicht gelöst. Zeigt dies an, dass stattdessen zu Atom gewechselt werden muss?
Der Qodesmith

1

Aufbauend auf der Antwort von @ blak3r, hier eine kurze Abkürzung, die ich für den Fall verwende, dass sie anderen Diagnosen hilft:

Wenn Sie versuchen, ein Node.js-Skript zu debuggen, dem die Dateideskriptoren ausgehen, finden Sie in der folgenden Zeile die Ausgabe des lsofvom betreffenden Knotenprozess verwendeten:

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

Dies wird synchron lsofgefiltert nach dem aktuell ausgeführten Node.js-Prozess ausgeführt und gibt die Ergebnisse über den Puffer zurück.

Verwenden Sie dann console.log(openFiles.toString()), um den Puffer in eine Zeichenfolge zu konvertieren und die Ergebnisse zu protokollieren.


0

cwait ist eine allgemeine Lösung zur Begrenzung der gleichzeitigen Ausführung von Funktionen, die Versprechen zurückgeben.

In Ihrem Fall könnte der Code ungefähr so ​​aussehen:

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

Für Nodemon- Benutzer: Verwenden Sie einfach das Flag --ignore , um das Problem zu lösen.

Beispiel:

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

0

Verwenden Sie die neueste fs-extra .

Ich hatte dieses Problem auf Ubuntu(16 und 18) mit viel Speicherplatz für Datei- / Socket-Deskriptoren (zählen mit lsof |wc -l). Gebrauchte fs-extraVersion 8.1.0. Nach dem Update auf 9.0.0"Fehler: EMFILE, zu viele geöffnete Dateien" verschwanden.

Ich habe auf verschiedenen Betriebssystemen verschiedene Probleme mit Knotenhandhabungsdateisystemen gehabt. Dateisysteme sind offensichtlich nicht trivial.


0

Ich hatte dieses Problem und habe es durch Laufen gelöst npm updateund es hat funktioniert.

In einigen Fällen müssen Sie möglicherweise node_modules entfernen rm -rf node_modules/

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.