Nœud et erreur: EMPLOI, trop de fichiers ouverts

Pendant quelques jours, j'ai cherché une solution de travail à une erreur

Error: EMFILE, too many open files

Il semble que beaucoup de personnes ont 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. Ceci est un peu étrange à mes yeux, car le nombre de fichiers que je manipule dans le répertoire est sous 10240. Même inconnu, 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 travail autour du 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. Qu'est-ce qui ne va pas avec ce code?

Une dernière question (je suis novice sur javascript et node), je suis en train de développer une application Web avec beaucoup de demandes pour environ 5000 utilisateurs quotidiens. J'ai plusieurs années d'expérience dans la programmation avec d'autres langues comme python et java. Alors je pensais initialement développer cette application avec django ou framework de jeu. Ensuite, j'ai découvert le nœud et je dois dire que l'idée d'un modèle d'E / S non bloquant est vraiment agréable, séduisante et surtout très rapide!

Mais quel genre de problèmes dois-je espérer avec le nœud? Est-ce un serveur web éprouvé? Quelles sont vos expériences?

L'utilisation du module graceful-fs par Isaac Schlueter (responsable de node.js) est probablement la solution la plus appropriée. Il fait un recul supplémentaire si EMFILE est rencontré. Il peut être utilisé comme un remplacement déroulant pour le module fs intégré.

Car quand gracieux-fs ne fonctionne pas … ou vous voulez juste comprendre d'où provient la fuite. Suivez ce processus.

(Par exemple, gracieux-fs ne va pas réparer 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 générera le nombre de poignées ouvertes 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 la 1024ème manche 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. J'espère que cela vous indique maintenant où regarder dans votre code pour la fuite.

Si vous ne connaissez pas plusieurs processus de nœud, la première recherche, 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 effectuant des recherches d'adresse IP, j'ai pu déterminer dans mon cas, il était publié par pubnub.

Référence de commande

Utilisez cette syntaxe pour déterminer le nombre de commandes ouvertes qu'un processus a ouvert …

Pour obtenir un compte de fichiers ouverts pour un certain pid

J'ai utilisé cette commande pour tester le nombre de fichiers qui ont été ouverts après avoir effectué différents é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 la limite de votre processus?

ulimit -a

La ligne que vous voulez ressemble à ceci: open files (-n) 1024

Modifiez définitivement la limite:

  • Testé sur Ubuntu 14.04, nodejs v. 7.9

Si vous prévoyez d'ouvrir plusieurs connexions (les pages Web sont un bon exemple), vous pouvez augmenter de façon permanente la limite:

  • 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 fermez-vous / connectez-vous à partir 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 différents uid.

J'ai rencontré ce problème aujourd'hui, et je n'ai trouvé aucune bonne solution pour cela, 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 } }); }); 

Vous lisez trop de fichiers à la fois. Le noeud lit les fichiers de manière asynchrone, de sorte que vous lisez tous les fichiers à la fois. Donc, vous lisez probablement 10240 à la fois.

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

Je viens de terminer l'écriture d'un petit extrait de code pour résoudre ce problème moi-même, toutes les autres solutions semblent trop lourdes et vous obliger à modifier la structure de votre programme.

Cette solution supprime uniquement les appels fs.readFile ou fs.writeFile pour qu'il n'y ait plus qu'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); }); } }; 

Avec la cornemuse, vous avez juste besoin de changer

 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

A 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 disparaissait.

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

Dans votre cas, le code pourrait être 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)); })