Tengo este código en un index.js que en su mayoría dejé sin modificar de la documentación oficial de Node.js.
const https = require('https'); const fs = require('fs'); const cluster = require('node:cluster'); const numCPUs = require('node:os').cpus().length; const process = require('node:process'); const livegame = require('./server-livegame'); const matchmaking = require('./server-matchmaking'); //Start ExpressJS var express = require('express'); const { match } = require('assert'); var app = express(); app.use(express.json()); if (cluster.isPrimary) { console.log(`Primary ${process.pid} is running`); for (let i = 0; i< numCPUs; i++){ cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`Worker ${worker.process.pid} died`); }); } else { new livegame; new matchmaking; }Aquí hay un código simplificado para el juego en vivo/emparejamiento que produce el error.
const https = require('https'); const fs = require('fs'); const mongoose = require('mongoose'); //Import Models const LiveMatch = require('./models/livematch'); //Start ExpressJS var express = require('express'); const { match } = require('assert'); //Interface Security var options = { key: fs.readFileSync('key.pem', 'utf8'), cert: fs.readFileSync('cert.pem', 'utf8') }; //Server var httpsServer = https.createServer(options, app); httpsServer.listen(443); var app = express(); app.use(express.json()); const chat = { puuid: String, name: String, roleID: String, message: String, } app.post(':id/chat', (req,res) => { //something here });Tengo el juego en vivo y el emparejamiento como archivos .js separados junto con index.js, a quienes llamo para iniciarlos mediante programación como instancias múltiples. Sin embargo, este es el error que me sale:
node:events:505 throw er; // Unhandled 'error' event ^ Error: listen EADDRINUSE: address already in use :::443 at Server.setupListenHandle [as _listen2] (node:net:1380:16) at listenInCluster (node:net:1428:12) at Server.listen (node:net:1516:7) at C:\Users\----\Documents\MG\src\server-matchmaking.js:25:66 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) Emitted 'error' event on Server instance at: at emitErrorNT (node:net:1407:8) at process.processTicksAndRejections (node:internal/process/task_queues:82:21) { code: 'EADDRINUSE', errno: -4091, syscall: 'listen', address: '::', port: 443 }Según mi progreso hasta ahora, creo que el problema es que yo cree más "espacio para comer" en lugar de contratar más "trabajadores".
¿Cómo creo correctamente instancias de un servidor con un equilibrio de carga adecuado mediante clústeres?
La agrupación en clústeres en nodejs, por su propia definición, es un grupo de procesos que están configurados para procesar las conexiones entrantes de la misma manera. El proceso maestro utiliza un algoritmo para rotar las conexiones entrantes entre los diversos procesos agrupados para que cada uno tenga un turno para manejar las conexiones entrantes. Esto generalmente se usa cuando necesita aumentar la cantidad de CPU que usa para procesar las solicitudes http entrantes. De forma predeterminada, un solo proceso de nodejs ejecuta su Javascript en un solo hilo/CPU.
Y, por definición, todos estos procesos agrupados están haciendo lo mismo (manejando las solicitudes http entrantes) que usted está haciendo en el proceso principal.
Si lo que realmente desea es ejecutar un código diferente (digamos que iba a realizar un procesamiento de imágenes intensivo) y, con razón, desea eliminar ese procesamiento intensivo de la CPU del subproceso principal para que pueda seguir respondiendo a las conexiones entrantes, entonces usted usaría WorkerThread (más subprocesos en el mismo proceso) o child_process (más procesos). En ambos casos, puede ejecutar cualquier código que desee en WorkerThread o child_process y puede ser completamente diferente de lo que está haciendo en su programa principal de nodejs.
Si el problema subyacente es algún procesamiento que está haciendo en un controlador POST en su servidor web, entonces si necesita o no investigar cualquiera de las formas anteriores de escalar depende completamente de lo que esté haciendo en ese controlador POST. Nodejs por sí mismo, con su modelo de E/S asíncrono (para redes, E/S de archivos, acceso a bases de datos, etc.) puede manejar una tonelada de solicitudes simultáneas sin involucrar subprocesos o procesos adicionales. El código asincrónico correctamente escrito escala muy bien en nodejs.
Por lo tanto, a menos que su proceso use mucho la CPU (como en el ejemplo de procesamiento de imágenes que usé anteriormente), primero debe escribir un buen código asíncrono, ver cómo funciona y tal vez probarlo para encontrar dónde están sus cuellos de botella. De hecho, puede encontrar que sus cuellos de botella son insignificantes (por lo tanto, puede obtener una escala bastante alta) o que no están en un lugar que se beneficiaría de los procesos adicionales anteriores y necesita atacar las causas específicas de los cuellos de botella. Con cualquier diseño a gran escala, primero implementa los conceptos básicos, luego instrumenta a escala para comprender sus cuellos de botella, medir nuevamente, trabajar en los cuellos de botella, medir nuevamente, etc.
Adivinar dónde están los cuellos de botella suele ser muy propenso a errores, lo que hace que coloque la ingeniería de software en los lugares equivocados. No adivines. Pruebe, mida, encuentre cuellos de botella, trabaje en cuellos de botella. Enjuague, haga espuma, repita.
Sobre la base de la publicación de @krtee:
El problema central aquí es que esencialmente está tratando de crear múltiples procesos en el mismo puerto.
¡Esto es cierto, y estás muy cerca de los documentos del nodo en esto!
// essentially all this stuff var httpsServer = https.createServer(options, app); httpsServer.listen(443); var app = express(); ... // needs to go in the `else` block of the worker initialization ... for (let i = 0; i< numCPUs; i++){ cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`Worker ${worker.process.pid} died`); }); } else { // right bout here. }Si bien es posible que no opte por la agrupación en clústeres, así es como arreglé este fragmento de código.
const https = require('https'); const fs = require('fs'); const cluster = require('node:cluster'); const numCPUs = require('node:os').cpus().length; const process = require('node:process'); const livegame = require('./server-livegame'); const matchmaking = require('./server-matchmaking'); //Start ExpressJS var express = require('express'); const { match } = require('assert'); var app = express(); app.use(express.json()); if (cluster.isPrimary) { console.log(`Primary ${process.pid} is running`); for (let i = 0; i< numCPUs; i++){ cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`Worker ${worker.process.pid} died`); }); } else { var httpsServer = https.createServer(options, app); httpsServer.listen(443); livegame(app); matchmaking(app); }con livegame/matchmaking.js luciendo como
const https = require('https'); const fs = require('fs'); const mongoose = require('mongoose'); //Import Models const LiveMatch = require('./models/livematch'); //Start ExpressJS var express = require('express'); const { match } = require('assert'); this.serverLiveGame = function(app) { const chat = { puuid: String, name: String, roleID: String, message: String, } app.post(':id/chat', (req,res) => { }); }; module.exports = this.serverLiveGame;Hubo más de un error en el código:
¡Gracias por toda la ayuda!