Introducción

El módulo cluster en Node.js permite la creación de procesos hijos que comparten el mismo puerto del servidor. Esto es útil para aprovechar al máximo los sistemas con múltiples núcleos de CPU, ya que Node.js es de un solo hilo por naturaleza. Al usar el módulo cluster, puedes crear múltiples instancias de tu aplicación que se ejecutan en paralelo, mejorando así el rendimiento y la capacidad de respuesta.

Conceptos Clave

  • Proceso Maestro (Master Process): El proceso principal que se encarga de crear y gestionar los procesos hijos (workers).
  • Proceso Hijo (Worker Process): Procesos secundarios que ejecutan el código de la aplicación y manejan las solicitudes entrantes.
  • Balanceo de Carga (Load Balancing): Distribuir las solicitudes entrantes entre los procesos hijos para optimizar el uso de recursos.

Ejemplo Práctico

Paso 1: Configuración del Proceso Maestro

Primero, necesitamos configurar el proceso maestro que creará y gestionará los procesos hijos.

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Crear un worker por cada CPU disponible
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died`);
  });
} else {
  // Los workers pueden compartir cualquier servidor TCP
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello World\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

Explicación del Código

  1. Importar Módulos:

    const cluster = require('cluster');
    const http = require('http');
    const numCPUs = require('os').cpus().length;
    

    Importamos los módulos necesarios: cluster para la gestión de procesos, http para crear el servidor y os para obtener el número de CPUs disponibles.

  2. Proceso Maestro:

    if (cluster.isMaster) {
      console.log(`Master ${process.pid} is running`);
    

    Verificamos si el proceso actual es el maestro. Si es así, imprimimos un mensaje indicando que el proceso maestro está en ejecución.

  3. Crear Workers:

    for (let i = 0; i < numCPUs; i++) {
      cluster.fork();
    }
    

    Creamos un worker por cada CPU disponible utilizando cluster.fork().

  4. Manejo de Eventos de Salida de Workers:

    cluster.on('exit', (worker, code, signal) => {
      console.log(`Worker ${worker.process.pid} died`);
    });
    

    Escuchamos el evento exit para detectar cuando un worker muere y registramos un mensaje en la consola.

  5. Proceso Worker:

    } else {
      http.createServer((req, res) => {
        res.writeHead(200);
        res.end('Hello World\n');
      }).listen(8000);
    
      console.log(`Worker ${process.pid} started`);
    }
    

    Si el proceso no es el maestro, entonces es un worker. Creamos un servidor HTTP que responde con "Hello World" y lo ponemos a escuchar en el puerto 8000.

Ejercicio Práctico

Ejercicio 1: Crear un Servidor HTTP con Cluster

  1. Objetivo: Crear un servidor HTTP que maneje solicitudes de manera eficiente utilizando el módulo cluster.
  2. Instrucciones:
    • Configura un proceso maestro que cree un worker por cada CPU disponible.
    • Cada worker debe manejar las solicitudes HTTP y responder con un mensaje personalizado.
    • Implementa un manejo básico de errores para reiniciar los workers que mueran.

Solución

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${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. Restarting...`);
    cluster.fork();
  });
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Hello from Worker ${process.pid}\n`);
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

Explicación de la Solución

  • Reinicio de Workers:
    cluster.on('exit', (worker, code, signal) => {
      console.log(`Worker ${worker.process.pid} died. Restarting...`);
      cluster.fork();
    });
    
    Cuando un worker muere, registramos un mensaje y creamos un nuevo worker para reemplazarlo.

Conclusión

El módulo cluster es una herramienta poderosa para mejorar el rendimiento de las aplicaciones Node.js en sistemas con múltiples núcleos de CPU. Al distribuir las solicitudes entre varios procesos hijos, puedes manejar más tráfico y mejorar la capacidad de respuesta de tu aplicación. En el siguiente tema, exploraremos los hilos de trabajo (worker threads) para tareas de computación intensiva.

Curso de Node.js

Módulo 1: Introducción a Node.js

Módulo 2: Conceptos Básicos

Módulo 3: Sistema de Archivos y E/S

Módulo 4: HTTP y Servidores Web

Módulo 5: NPM y Gestión de Paquetes

Módulo 6: Framework Express.js

Módulo 7: Bases de Datos y ORMs

Módulo 8: Autenticación y Autorización

Módulo 9: Pruebas y Depuración

Módulo 10: Temas Avanzados

Módulo 11: Despliegue y DevOps

Módulo 12: Proyectos del Mundo Real

© Copyright 2024. Todos los derechos reservados