Depuis quelques jours, je cherche une solution de travail à une erreur
Error: EMFILE, too many open files
Il semble que beaucoup de gens ont le même problème. La réponse habituelle consiste à augmenter le nombre de descripteurs de fichiers. Donc, 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 traite 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 fichier .
Deuxième question:
Après plusieurs 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… .. Qu'est-ce qui ne va pas avec ce code?
Une dernière question (je suis nouveau dans le javascript et le noeud), 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 langages tels que Python et Java. donc à l'origine, j'ai pensé développer cette application avec Django ou un framework de jeu. Ensuite, j'ai découvert node 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 type de problèmes dois-je m'attendre avec node? Est-ce un serveur web éprouvé en production? Quelles sont vos expériences?
Car quand graceful-fs ne fonctionne pas ... ou si vous voulez simplement comprendre d'où vient la fuite. Suivez ce processus.
(par exemple, graceful-fs ne va pas réparer votre wagon si votre problème concerne les sockets.)
Article de mon blog: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html
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 que: 1023u (dernière ligne) - il s'agit du 1024ème descripteur de fichier, qui correspond au maximum par défaut.
Maintenant, regardez la dernière colonne. Cela indique quelle ressource est ouverte. Vous verrez probablement un certain nombre de lignes ayant toutes le même nom de ressource. Espérons que cela vous indique maintenant où chercher dans votre code pour la fuite.
Si vous ne connaissez pas plusieurs processus de nœud, commencez par rechercher quel processus est associé au 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'étais en mesure de déterminer dans mon cas qu'il était lié à pubnub.
Utilisez cette syntaxe pour déterminer le nombre de poignées ouvertes qu'un processus a ouvertes ...
J'ai utilisé cette commande pour tester le nombre de fichiers ouverts après 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
ulimit -a
La ligne que vous voulez ressemblera à ceci:
open files (-n) 1024
Si vous prévoyez d'ouvrir de nombreuses connexions (les websockets en sont un bon exemple), vous pouvez augmenter la limite de façon permanente:
fichier: /etc/pam.d/common-session (ajouter à la fin)} _
session required pam_limits.so
fichier: /etc/security/limits.conf (ajoutez à la fin ou modifiez s'il existe déjà))
root soft nofile 40000
root hard nofile 100000
redémarrez votre nodejs et déconnectez-vous/connectez-vous à partir de ssh.
Utiliser le module graceful-fs
d’Isaac Schlueter (responsable de node.js) est probablement la solution la plus appropriée. Il effectue un recul progressif si un fichier EMFILE est rencontré. Il peut être utilisé en remplacement immédiat du module fs
intégré.
J'ai rencontré ce problème aujourd'hui et, ne trouvant pas de bonne solution, j'ai créé un module pour le résoudre. J'ai été inspiré par l'extrait de @ fbartho, mais je voulais éviter de remplacer 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. Le noeud lit les fichiers de manière asynchrone, il lira tous les fichiers à la fois. Donc, vous lisez probablement la limite 10240.
Voir 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 juste de finir d'écrire un petit bout de code pour résoudre ce problème moi-même. Toutes les autres solutions semblent bien trop lourdes et vous obligent à changer la structure de votre programme.
Cette solution bloque uniquement les appels fs.readFile ou fs.writeFile, de sorte qu’il n’ya 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);
});
}
};
Avait le même problème lors de l'exécution de la commande nodemon donc j'ai réduit le nom des fichiers ouverts dans sublime text et l'erreur a disparu.
Je ne suis pas sûr que cela puisse aider quelqu'un, j'ai commencé à travailler sur un gros projet avec beaucoup de dépendances qui m'a jeté la même erreur. Mon collègue m'a suggéré d'installer watchman
en utilisant brasser et cela a résolu ce problème pour moi.
brew update
brew install watchman
Comme nous tous, vous êtes une autre victime d'E/S asynchrones. Avec les appels asynchrones, si vous faites une boucle autour d’un grand nombre de fichiers, Node.js commencera à ouvrir un descripteur de fichier pour chaque fichier à lire, puis attendra une action jusqu’à sa fermeture.
Le descripteur de fichier reste ouvert jusqu'à ce qu'une ressource soit disponible sur votre serveur pour la lire. Même si vos fichiers sont petits et que la lecture ou la mise à jour est rapide, cela prend un certain 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 avec une limite, puis vous ajoutez des noms de fichier à la file.
Remarque: Si vous devez ouvrir plusieurs fichiers, il serait judicieux 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 constater 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 que vous avez définie précédemment.
async.queue obtient des informations sur la disponibilité de la file d'attente par le biais d'un rappel. Ce rappel n'est appelé que lorsque le fichier de données est lu et que toutes les actions que vous devez effectuer sont effectuées. (voir méthode fileRead)
Donc, vous ne pouvez 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
Avec cornemuse, vous avez 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
cwait est une solution générale pour limiter les exécutions simultanées de toutes les fonctions renvoyant 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));
})
En s'appuyant sur la réponse de @ blak3r, voici un peu de raccourci que j'utilise au cas où cela aiderait les autres à diagnostiquer:
Si vous essayez de déboguer un script Node.js qui manque de descripteurs de fichier, voici une ligne pour vous donner le résultat de lsof
utilisé par le processus de noeud en question:
openFiles = child_process.execSync(`lsof -p ${process.pid}`);
Ceci sera exécuté de manière synchrone lsof
filtré par le processus Node.js en cours d’exécution et renverra les résultats via le tampon.
Puis utilisez console.log(openFiles.toString())
pour convertir le tampon en chaîne et consigner les résultats.