Tengo un clúster de nodejs con un principal que maneja los ciclos de los trabajadores (en el ciclo while) y que escucha los mensajes de los trabajadores para progresar en el ciclo. (En mi código, index.js no envía mensajes en setInterval sino en otro tipo de evento, aquí he simplificado el código para obtener la esencia del problema)
Servidor.js
var cluster = require('cluster'); const ClusterMessages = require('cluster-messages'); const messages = new ClusterMessages(); if (cluster.isMaster){ let worker = cluster.fork() console.log(cluster); (async ()=>{ let cycle = 0 while(true){ console.log(cycle); cycle ++ await Promise.all([ enough(), ]) } function enough () { return new Promise(resolve => { messages.on('enough', () => { console.log('enough'); resolve() }); }); }})() } else { require('./index.js') }Índice.js
const ClusterMessages = require('cluster-messages'); const messages = new ClusterMessages(); setInterval(() => { messages.send('enough'); }, 1000);El código funciona bien (como tal, en este ejemplo y en mi código), pero parece haber una pérdida de memoria, como puede comprender a partir del resultado de este código:
0 enough 1 enough enough 2 enough enough enough 3 enough enough enough enough...Intenté varias cosas, como intercambiar una nueva promesa y mensajes. on(), agregar un retorno en la devolución de llamada de la promesa, pero no tengo idea de lo que está sucediendo aquí. ¿Algunas ideas?
La solución es crear otro evento que pueda activarse una vez contrario al 'escucha de eventos' del paquete de mensajes de clúster.
Servidor.js
if (cluster.isMaster){ let worker = cluster.fork() console.log(cluster); // Importing events const EventEmitter = require('events'); const eventEmitter = new EventEmitter(); messages.on('enough', () => { eventEmitter.emit('event'); }); (async ()=>{let cycle = 0 while(true){ console.log(cycle); cycle ++ await Promise.all([ enough(), ]) } function enough () { return new Promise(resolve => { eventEmitter.once('event', () => { console.log('event'); resolve() }); }); }})() } else { require('./index.js') }Índice.js
const ClusterMessages = require('cluster-messages'); const messages = new ClusterMessages(); setInterval(() => { messages.send('enough'); }, 1000);Cada llamada enough() instala otro oyente para el evento enough en los messages . Nunca se eliminan, lo que provoca una fuga de memoria (lo que genera un número cada vez mayor de registros por evento). En su lugar, utilice el método de once para instalar el oyente:
function enough () { return new Promise(resolve => { messages.once('enough', () => { // ^^^^ console.log('enough'); resolve(); }); }); } O incluso más simple, usando once :
const { once } = require('events'); function enough() { return once(messages, 'enough'); }En su ejemplo particular, recomendaría no usar promesas para manejar los eventos. Incluso puede perder eventos que se activan mientras elimina y vuelve a conectar un oyente. Solo escribe
let cycle = 0 messages.on('enough', () => { console.log(cycle); cycle++; }); Si por alguna razón necesita un bucle del que pueda romper o await otras cosas, recomendaría un iterador asíncrono, construido on :
const { once } = require('events'); (async () => { let cycle = 0 for await (const _ of on(messages, 'enough')) { console.log(cycle); cycle++; } })();