nœud et erreur: EMFILE, trop de fichiers ouverts

166

Depuis quelques jours, je cherche une solution fonctionnelle à une erreur

Error: EMFILE, too many open files

Il semble que de nombreuses personnes aient le même problème. La réponse habituelle consiste à augmenter le nombre de descripteurs de fichiers. Alors, j'ai essayé ceci:

sysctl -w kern.maxfiles=20480,

La valeur par défaut est 10240. C'est un peu étrange à mes yeux, car le nombre de fichiers que je gère dans le répertoire est inférieur à 10240. Encore plus étrange, je reçois toujours la même erreur après avoir augmenté le nombre de descripteurs de fichiers .

Deuxième question:

Après un certain nombre de recherches, j'ai trouvé un moyen de contourner le problème "trop ​​de fichiers ouverts":

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

Malheureusement, je reçois toujours la même erreur. Quel est le problème avec ce code?

Une dernière question (je suis nouveau dans javascript et node), je suis en train de développer une application web avec beaucoup de requêtes pour environ 5000 utilisateurs quotidiens. J'ai de nombreuses années d'expérience en programmation avec d'autres langages comme python et java. donc à l'origine j'ai pensé développer cette application avec django ou play framework. Puis j'ai découvert node et je dois dire que l'idée de modèle d'E / S non bloquant est vraiment sympa, séduisante, et surtout très rapide!

Mais à quel genre de problèmes dois-je m'attendre avec node? S'agit-il d'un serveur Web éprouvé en production? Quelles sont vos expériences?

xaverras
la source

Réponses:

83

Pour quand graceful-fs ne fonctionne pas ... ou que vous voulez simplement comprendre d'où vient la fuite. Suivez ce processus.

(par exemple, graceful-fs ne réparera pas votre wagon si votre problème concerne les sockets.)

De mon article de blog: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

Comment isoler

Cette commande affichera le nombre de descripteurs ouverts pour les processus 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)
....

Notez le: 1023u (dernière ligne) - c'est le 1024ème descripteur de fichier qui est le maximum par défaut.

Maintenant, regardez la dernière colonne. Cela indique quelle ressource est ouverte. Vous verrez probablement un certain nombre de lignes toutes avec le même nom de ressource. Espérons que cela vous indique maintenant où chercher dans votre code la fuite.

Si vous ne connaissez pas plusieurs processus de nœuds, recherchez d'abord quel processus a le pid 12211. Cela vous indiquera le processus.

Dans mon cas ci-dessus, j'ai remarqué qu'il y avait un tas d'adresses IP très similaires. Ils étaient tous 54.236.3.### en faisant des recherches d'adresse IP, a pu déterminer dans mon cas qu'il était lié à pubnub.

Référence de commande

Utilisez cette syntaxe pour déterminer le nombre de descripteurs ouverts qu'un processus a ouverts ...

Pour obtenir le nombre de fichiers ouverts pour un certain pid

J'ai utilisé cette commande pour tester le nombre de fichiers ouverts après avoir effectué divers événements dans mon application.

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

Quelle est votre limite de processus?

ulimit -a

La ligne que vous voulez ressemblera à ceci:

open files                      (-n) 1024

Modifier définitivement la limite:

  • testé sur Ubuntu 14.04, nodejs v.7.9

Dans le cas où vous prévoyez d'ouvrir de nombreuses connexions (Websockets est un bon exemple), vous pouvez augmenter la limite de manière permanente:

  • fichier: /etc/pam.d/common-session (ajouter à la fin)

    session required pam_limits.so
  • fichier: /etc/security/limits.conf (ajouter à la fin, ou modifier s'il existe déjà)

    root soft  nofile 40000
    root hard  nofile 100000
    
  • redémarrez votre nodejs et déconnectez / connectez-vous de ssh.

  • cela peut ne pas fonctionner pour les anciens NodeJS, vous devrez redémarrer le serveur
  • utilisez plutôt que si votre nœud fonctionne avec un uid différent.
blak3r
la source
1
Comment pouvez-vous modifier la limite de fichiers ouverts?
Om3ga
13
ulimit -n 2048 pour permettre l'ouverture de 2048 fichiers
Gaël Barbin
1
C'est la réponse la plus descriptive et la plus correcte. Je vous remercie!
Kostanos
J'ai des nombres rares. lsof -i -n -P | grep "12843" | wc -l== 4085 mais ulimit -a | grep "open files"== (-n) 1024 un indice comment je pourrais avoir plus de fichiers ouverts que la limite maximale?
Kostanos
1
Puisque le blog de @ blak3r semble être en panne, voici un lien vers son article sur la machine de retour. web.archive.org/web/20140508165434/http://... Super utile et une très bonne lecture!
James
72

L'utilisation du graceful-fsmodule d'Isaac Schlueter (mainteneur de node.js) est probablement la solution la plus appropriée. Il effectue un recul incrémentiel si EMFILE est rencontré. Il peut être utilisé en remplacement du fsmodule intégré.

Myrne Stol
la source
2
M'a sauvé, pourquoi n'est-ce pas le nœud par défaut? Pourquoi dois-je installer un plugin tiers pour résoudre le problème?
Anthony Webb
7
Je pense que, d'une manière générale, Node essaie d'exposer le plus possible à l'utilisateur. Cela donne à tout le monde (pas seulement aux développeurs principaux de Node) la possibilité de résoudre tout problème découlant de l'utilisation de cette interface relativement brute. Dans le même temps, il est très facile de publier des solutions et de télécharger celles publiées par d'autres via npm. Ne vous attendez pas à beaucoup d'intelligence de Node lui-même. Au lieu de cela, attendez-vous à trouver l'intelligence dans les packages publiés sur npm.
Myrne Stol
5
C'est bien si c'est votre propre code, mais de nombreux modules npm ne l'utilisent pas.
UpTheCreek
1
Ce module a résolu tous mes problèmes! Je conviens que le nœud semble encore un peu brut, mais principalement parce qu'il est vraiment difficile de comprendre ce qui ne va pas avec si peu de documentation et les bonnes solutions acceptées aux problèmes connus.
sidonaldson
comment tu le npm ? comment puis-je combiner cela dans mon code au lieu des fs réguliers?
Aviram Netanel le
11

Je ne sais pas si cela aidera quelqu'un, j'ai commencé à travailler sur un gros projet avec beaucoup de dépendances qui m'ont renvoyé la même erreur. Mon collègue m'a suggéré d'installer en watchmanutilisant brew et cela a résolu ce problème pour moi.

brew update
brew install watchman

Edit le 26 juin 2019: lien Github vers watchman

bh4r4th
la source
Cela m'a au moins aidé. Sur un projet natif de réaction, le bundler peut soit ouvrir les fichiers en mode natif, soit (s'il est installé) utiliser watchman pour le faire d'une manière plus agréable pour le système d'exploitation. Cela peut donc être d'une grande aide - il est documenté dans le Quickstart CLI natif de réaction pour macOS, même: facebook.github.io/react-native/docs/getting-started.html - bravo!
Mike Hardy le
7

Je suis tombé sur ce problème aujourd'hui, et ne trouvant pas de bonnes solutions, j'ai créé un module pour y remédier. J'ai été inspiré par l'extrait de @ fbartho, mais je voulais éviter d'écraser le module fs.

Le module que j'ai écrit est Filequeue , et vous l'utilisez comme 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
la source
7

Vous lisez trop de fichiers. Node lit les fichiers de manière asynchrone, il lira tous les fichiers à la fois. Vous lisez donc probablement la limite de 10240.

Voyez si cela fonctionne:

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.
la source
6

Comme nous tous, vous êtes une autre victime d'E / S asynchrones. Avec les appels asynchrones, si vous bouclez autour d'un grand nombre de fichiers, Node.js commencera à ouvrir un descripteur de fichier pour chaque fichier à lire, puis attendra l'action jusqu'à ce que vous le fermiez.

Le descripteur de fichier reste ouvert jusqu'à ce que la ressource soit disponible sur votre serveur pour le lire. Même si vos fichiers sont petits et que la lecture ou la mise à jour est rapide, cela prend du temps, mais dans le même temps, votre boucle ne s'arrête pas pour ouvrir un nouveau descripteur de fichiers. Donc si vous avez trop de fichiers, la limite sera bientôt atteinte et vous obtiendrez un magnifique EMFILE .

Il existe une solution, créer une file d'attente pour éviter cet effet.

Merci aux personnes qui ont écrit Async , il existe une fonction très utile pour cela. Il existe une méthode appelée Async.queue , vous créez une nouvelle file d'attente avec une limite, puis ajoutez des noms de fichiers à la file d'attente.

Remarque: si vous devez ouvrir de nombreux fichiers, ce serait une bonne idée de stocker les fichiers actuellement ouverts et de ne pas les rouvrir indéfiniment.

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

Vous pouvez voir que chaque fichier est ajouté à la file d'attente (nom de fichier console.log), mais uniquement lorsque la file d'attente actuelle est inférieure à la limite définie précédemment.

async.queue obtenir des informations sur la disponibilité de la file d'attente via un rappel, ce rappel n'est appelé que lorsque le fichier de données est lu et que toute action que vous avez à faire est réalisée. (voir méthode fileRead)

Vous ne pouvez donc pas être submergé par le descripteur de fichiers.

> 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
la source
3

Je viens de terminer d'écrire un petit bout de code pour résoudre moi-même ce problème, toutes les autres solutions semblent beaucoup trop lourdes et vous obligent à changer la structure de votre programme.

Cette solution bloque simplement tous les appels fs.readFile ou fs.writeFile de sorte qu'il n'y ait pas plus d'un nombre défini en vol à un moment donné.

// 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
la source
Vous devriez faire un dépôt pour cela sur github.
Nick le
Cela fonctionne très bien si graceful-fs ne fonctionne pas pour vous.
Ceekay
3

J'ai fait toutes les choses mentionnées ci-dessus pour le même problème mais rien n'a fonctionné. J'ai essayé ci-dessous, cela fonctionnait à 100%. Changements de configuration simples.

Option 1 définir la limite (cela ne fonctionnera pas la plupart du temps)

user@ubuntu:~$ ulimit -n 65535

vérifier la limite disponible

user@ubuntu:~$ ulimit -n
1024

Option 2 Augmenter la limite disponible pour dire 65535

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

ajoutez-y la ligne suivante

fs.file-max = 65535

exécutez ceci pour actualiser avec une nouvelle configuration

user@ubuntu:~$ sudo sysctl -p

éditer le fichier suivant

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

y ajouter les lignes suivantes

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

éditer le fichier suivant

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

ajoutez-y cette ligne

session required pam_limits.so

déconnectez-vous et connectez-vous et essayez la commande suivante

user@ubuntu:~$ ulimit -n
65535

Option 3 Il suffit d'ajouter ci-dessous la ligne dans

DefaultLimitNOFILE=65535

vers /etc/systemd/system.conf et /etc/systemd/user.conf

Rohit Parte
la source
l'option 2 est plutôt longue et j'espère que l'option 3 fonctionnera, mais ce n'est pas pour mon ubuntu 18
eugene
1

Avec la cornemuse, tu as juste besoin de changement

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

La cornemuse vous aide à limiter le parallèle. plus de détails: https://github.com/JacksonTian/bagpipe

user1837639
la source
Tout est en chinois ou dans une autre langue asiatique. Existe-t-il une documentation rédigée en anglais?
Fatih Arslan
@FatihArslan English doc est disponible maintenant.
user1837639
1

J'ai eu le même problème lors de l'exécution de la commande nodemon , j'ai donc réduit le nom des fichiers ouverts dans un texte sublime et l'erreur a disparu.

Buhiire Keneth
la source
Moi aussi, je recevais des EMFILEerreurs et, par essais et erreurs , j'ai remarqué que la fermeture de certaines fenêtres Sublime résolvait le problème. Je ne sais toujours pas pourquoi. J'ai essayé d'ajouter ulimit -n 2560à mon .bash_profile, mais cela n'a pas résolu le problème. Cela indique-t-il la nécessité de passer à Atom à la place?
The Qodesmith
1

En s'appuyant sur la réponse de @ blak3r, voici un raccourci que j'utilise au cas où cela aiderait d'autres diagnostics:

Si vous essayez de déboguer un script Node.js qui manque de descripteurs de fichier, voici une ligne pour vous donner la sortie lsofutilisée par le processus de nœud en question:

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

Cela fonctionnera de manière synchrone lsof filtrée par le processus Node.js en cours d'exécution et retournera les résultats via un tampon.

Utilisez ensuite console.log(openFiles.toString())pour convertir le tampon en chaîne et consigner les résultats.

James
la source
0

cwait est une solution générale pour limiter les exécutions simultanées de toutes les fonctions qui renvoient des promesses.

Dans votre cas, le code pourrait être quelque chose comme:

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
la source
0

Pour les utilisateurs de nodemon : utilisez simplement l' indicateur --ignore pour résoudre le problème.

Exemple:

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

Utilisez le dernier fs-extra.

J'ai eu ce problème sur Ubuntu(16 et 18) avec beaucoup d'espace pour les descripteurs de fichier / socket (compter avec lsof |wc -l). fs-extraVersion d' occasion 8.1.0. Après la mise à jour de 9.0.0"Erreur: EMFILE, trop de fichiers ouverts" a disparu.

J'ai rencontré divers problèmes sur divers systèmes d'exploitation avec des systèmes de fichiers de gestion de nœuds. Les systèmes de fichiers ne sont évidemment pas triviaux.

dr0i
la source
0

J'ai eu ce problème, et je l'ai résolu en exécutant npm updateet cela a fonctionné.

Dans certains cas, vous devrez peut-être supprimer node_modules rm -rf node_modules/

Adnane Lamghari
la source
0

J'ai installé watchman, modifié la limite, etc. et cela n'a pas fonctionné dans Gulp.

Le redémarrage d'iterm2 a cependant aidé.

Runnick
la source