nodo y Error: EMFILE, demasiados archivos abiertos

166

Durante algunos días he buscado una solución funcional a un error

Error: EMFILE, too many open files

Parece que muchas personas tienen el mismo problema. La respuesta habitual implica aumentar el número de descriptores de archivo. Entonces, he intentado esto:

sysctl -w kern.maxfiles=20480,

El valor predeterminado es 10240. Esto es un poco extraño a mis ojos, porque la cantidad de archivos que manejo en el directorio es inferior a 10240. Aún más extraño, sigo recibiendo el mismo error después de aumentar la cantidad de descriptores de archivo .

Segunda pregunta:

Después de varias búsquedas, encontré una solución para el problema de "demasiados archivos abiertos":

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

Lamentablemente, sigo recibiendo el mismo error. ¿Qué está mal con este código?

Una última pregunta (soy nuevo en javascript y nodo), estoy en el proceso de desarrollar una aplicación web con muchas solicitudes para aproximadamente 5000 usuarios diarios. Tengo muchos años de experiencia en programación con otros lenguajes como Python y Java. originalmente pensé en desarrollar esta aplicación con django o play framework. Luego descubrí el nodo y debo decir que la idea del modelo de E / S sin bloqueo es realmente agradable, seductora y, sobre todo, muy rápida.

Pero, ¿qué tipo de problemas debo esperar con el nodo? ¿Es un servidor web probado en producción? Cuales son tus experiencias

xaverras
fuente

Respuestas:

83

Para cuando graceful-fs no funciona ... o simplemente quieres entender de dónde viene la fuga. Sigue este proceso.

(por ejemplo, graceful-fs no arreglará su vagón si su problema es con los enchufes).

Del artículo de mi blog: http://www.blakerobertson.com/devlog/2014/1/11/how-to-determine-whats-causing-error-connect-emfile-nodejs.html

Cómo aislar

Este comando generará el número de identificadores abiertos para los procesos de 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)
....

Observe el: 1023u (última línea) : ese es el identificador de archivo 1024 que es el máximo predeterminado.

Ahora, mira la última columna. Eso indica qué recurso está abierto. Probablemente verá varias líneas, todas con el mismo nombre de recurso. Con suerte, eso ahora te dice dónde buscar en tu código la fuga.

Si no conoce procesos de múltiples nodos, primero busque qué proceso tiene pid 12211. Eso le dirá el proceso.

En mi caso anterior, noté que había un montón de direcciones IP muy similares. Todos estaban 54.236.3.### haciendo búsquedas de direcciones IP, pude determinar en mi caso que estaba relacionado con pubnub.

Referencia de comando

Use esta sintaxis para determinar cuántos identificadores abiertos tiene abierto un proceso ...

Para obtener un recuento de archivos abiertos para un determinado pid

Utilicé este comando para probar la cantidad de archivos que se abrieron después de realizar varios eventos en mi aplicación.

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

¿Cuál es su límite de proceso?

ulimit -a

La línea que desea se verá así:

open files                      (-n) 1024

Cambiar permanentemente el límite:

  • probado en Ubuntu 14.04, nodejs v. 7.9

En caso de que espere abrir muchas conexiones (websockets es un buen ejemplo), puede aumentar permanentemente el límite:

  • archivo: /etc/pam.d/common-session (agregar al final)

    session required pam_limits.so
  • archivo: /etc/security/limits.conf (agregar al final o editar si ya existe)

    root soft  nofile 40000
    root hard  nofile 100000
  • reinicie sus nodejs y cierre sesión / inicie sesión desde ssh.

  • Esto puede no funcionar para los NodeJS más antiguos, necesitará reiniciar el servidor
  • use en lugar de si su nodo se ejecuta con uid diferente
blak3r
fuente
1
¿Cómo se puede cambiar el límite de archivos abiertos?
Om3ga
13
ulimit -n 2048 para permitir 2048 archivos abiertos
Gaël Barbin
1
Esta es la respuesta más descriptiva y correcta. ¡Gracias!
Kostanos
Tengo números raros. lsof -i -n -P | grep "12843" | wc -l== 4085 pero ulimit -a | grep "open files"== (-n) 1024 ¿ alguna pista de cómo podría tener más archivos abiertos que el límite máximo?
Kostanos
1
Dado que el blog de @ blak3r parece estar inactivo, aquí hay un enlace a su artículo sobre la máquina de retroceso. web.archive.org/web/20140508165434/http://… ¡ Súper útil y una lectura realmente genial!
James
72

Usar el graceful-fsmódulo de Isaac Schlueter (mantenedor de node.js) es probablemente la solución más adecuada. Realiza un retroceso incremental si se encuentra EMFILE. Se puede usar como un reemplazo directo para el fsmódulo incorporado .

Myrne Stol
fuente
2
Me salvó, ¿por qué este no es el nodo predeterminado? ¿Por qué necesito instalar algún complemento de terceros para resolver el problema?
Anthony Webb
77
Creo que, en términos generales, Node intenta exponer lo más posible al usuario. Esto les da a todos (no solo a los desarrolladores principales de Node) la oportunidad de resolver cualquier problema que surja del uso de esta interfaz relativamente cruda. Al mismo tiempo, es realmente fácil publicar soluciones y descargar las publicadas por otros a través de npm. No esperes mucha inteligencia del propio Nodo. En cambio, espere encontrar la inteligencia en los paquetes publicados en npm.
Myrne Stol
55
Está bien si es su propio código, pero muchos módulos npm no lo usan.
UpTheCreek
1
¡Este módulo resolvió todos mis problemas! Estoy de acuerdo en que el nodo parece estar todavía un poco en bruto, pero principalmente porque es realmente difícil entender qué está mal con tan poca documentación y soluciones correctas aceptadas para problemas conocidos.
sidonaldson
¿cómo NPM es? ¿Cómo combino esto en mi código en lugar de los fs regulares?
Aviram Netanel
11

No estoy seguro de si esto ayudará a alguien, comencé a trabajar en un gran proyecto con muchas dependencias que me arrojó el mismo error. Mi colega me sugirió instalar watchmancon brew y eso solucionó este problema.

brew update
brew install watchman

Edición el 26 de junio de 2019: enlace de Github a watchman

bh4r4th
fuente
Esto me ayudó al menos. En un proyecto de reacción nativa, el paquete puede abrir los archivos de forma nativa o (si está instalado) usar watchman para hacerlo de una manera que sea más agradable para el sistema operativo. Por lo tanto, puede ser de gran ayuda: está documentado en el inicio rápido de CLI react-native para macOS, incluso: facebook.github.io/react-native/docs/getting-started.html - ¡salud!
Mike Hardy
7

Me encontré con este problema hoy, y al no encontrar buenas soluciones para él, creé un módulo para abordarlo. Me inspiró el fragmento de @fbartho, pero quería evitar sobrescribir el módulo fs.

El módulo que escribí es Filequeue , y lo usas como 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
fuente
7

Estás leyendo demasiados archivos. El nodo lee los archivos de forma asincrónica, leerá todos los archivos a la vez. Entonces probablemente estés leyendo el límite 10240.

Vea si esto funciona:

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.
fuente
6

Como todos nosotros, usted es otra víctima de E / S asíncrona. Con llamadas asíncronas, si recorre muchos archivos, Node.js comenzará a abrir un descriptor de archivo para cada archivo a leer y luego esperará la acción hasta que lo cierre.

El descriptor de archivo permanece abierto hasta que el recurso esté disponible en su servidor para leerlo. Incluso si sus archivos son pequeños y la lectura o actualización es rápida, lleva algo de tiempo, pero al mismo tiempo su ciclo no se detiene para abrir el nuevo descriptor de archivos. Entonces, si tiene demasiados archivos, pronto se alcanzará el límite y obtendrá un hermoso EMFILE .

Hay una solución, crear una cola para evitar este efecto.

Gracias a las personas que escribieron Async , hay una función muy útil para eso. Hay un método llamado Async.queue , crea una nueva cola con un límite y luego agrega nombres de archivo a la cola.

Nota: Si tiene que abrir muchos archivos, sería una buena idea almacenar qué archivos están actualmente abiertos y no volver a abrirlos infinitamente.

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

Puede ver que cada archivo se agrega a la cola (nombre de archivo console.log), pero solo cuando la cola actual está por debajo del límite establecido previamente.

async.queue obtiene información sobre la disponibilidad de la cola a través de una devolución de llamada, esta devolución de llamada solo se llama cuando se lee el archivo de datos y se logra cualquier acción que tenga que hacer. (ver método fileRead)

Por lo tanto, no puede sentirse abrumado por el descriptor de archivos.

> 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
fuente
3

Acabo de terminar de escribir un pequeño fragmento de código para resolver este problema yo mismo, todas las otras soluciones parecen demasiado pesadas y requieren que cambie la estructura de su programa.

Esta solución solo detiene cualquier llamada fs.readFile o fs.writeFile para que no haya más de un número establecido en vuelo en un momento dado.

// 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
fuente
Deberías hacer un repositorio para esto en github.
Nick
Esto funciona muy bien si graceful-fs no funciona para usted.
Ceekay
3

Hice todo lo mencionado anteriormente para el mismo problema, pero nada funcionó. Lo intenté a continuación, funcionó al 100%. Cambios de configuración simples.

Opción 1 establecer límite (no funcionará la mayor parte del tiempo)

user@ubuntu:~$ ulimit -n 65535

consultar límite disponible

user@ubuntu:~$ ulimit -n
1024

Opción 2 Para aumentar el límite disponible para decir 65535

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

agregue la siguiente línea

fs.file-max = 65535

ejecutar esto para actualizar con nueva configuración

user@ubuntu:~$ sudo sysctl -p

edite el siguiente archivo

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

agregue las siguientes líneas

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

edite el siguiente archivo

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

agregue esta línea

session required pam_limits.so

cerrar sesión e iniciar sesión y probar el siguiente comando

user@ubuntu:~$ ulimit -n
65535

Opción 3 Simplemente agregue la siguiente línea en

DefaultLimitNOFILE=65535

a /etc/systemd/system.conf y /etc/systemd/user.conf

Rohit Parte
fuente
la opción 2 es bastante larga, y esperaba que la opción 3 funcione, pero no es para mi ubuntu 18
eugene
1

Con la gaita, solo necesitas cambiar

FS.readFile(filename, onRealRead);

=>

var bagpipe = new Bagpipe(10);

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

La gaita te ayuda a limitar el paralelo. Más detalles: https://github.com/JacksonTian/bagpipe

usuario1837639
fuente
Todo está en chino u otro idioma asiático. ¿Hay alguna documentación escrita en inglés?
Fatih Arslan
@FatihArslan English doc ya está disponible.
user1837639
1

Tuve el mismo problema al ejecutar el comando nodemon, así que reduje el nombre de los archivos abiertos en texto sublime y el error desapareció.

Buhiire Keneth
fuente
Yo también recibía EMFILEerrores y, mediante prueba y error, noté que al cerrar algunas ventanas Sublime se resolvió el problema. Aún no sé por qué. Intenté agregar ulimit -n 2560a mi .bash_profile, pero eso no resolvió el problema. ¿Indica esto la necesidad de cambiar a Atom ?
The Qodesmith
1

Sobre la base de la respuesta de @ blak3r, aquí hay un poco de taquigrafía que uso en caso de que ayude a otros diagnósticos:

Si está tratando de depurar un script Node.js que se está quedando sin descriptores de archivo, aquí hay una línea para proporcionarle la salida lsofutilizada por el proceso de nodo en cuestión:

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

Esto se ejecutará sincrónicamente lsoffiltrado por el proceso actual Node.js en ejecución y devolverá los resultados a través del búfer.

Luego, use console.log(openFiles.toString())para convertir el búfer en una cadena y registre los resultados.

James
fuente
0

cwait es una solución general para limitar las ejecuciones concurrentes de cualquier función que devuelva promesas.

En su caso, el código podría ser algo como:

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
fuente
0

Para usuarios de nodemon : simplemente use el indicador --ignore para resolver el problema.

Ejemplo:

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

Utiliza lo último fs-extra.

Tuve ese problema en Ubuntu(16 y 18) con un montón de espacio de descriptores de archivo / socket (contar con lsof |wc -l). Usado fs-extraversión 8.1.0. Después de la actualización de 9.0.0"Error: EMFILE, demasiados archivos abiertos" desapareció.

He experimentado diversos problemas en diversos sistemas operativos con sistemas de archivos de manejo de nodos. Los sistemas de archivos obviamente no son triviales.

dr0i
fuente
0

Tuve este problema, lo resolví ejecutando npm updatey funcionó.

En algunos casos, es posible que deba eliminar node_modules rm -rf node_modules/

Adnane Lamghari
fuente
0

Instalé watchman, cambié el límite, etc. y no funcionó en Gulp.

Sin embargo, reiniciar iterm2 realmente ayudó.

Runnick
fuente