Escalando horizontalmente
Ahora que nuestra aplicación es resiliente a interrupciones temporales de red, veamos cómo podemos escalarla horizontalmente para poder soportar miles de clientes concurrentes.
- Escalado horizontal (también conocido como "scaling out") significa añadir nuevos servidores a tu infraestructura para hacer frente a nuevas demandas
- Escalado vertical (también conocido como "scaling up") significa añadir más recursos (poder de procesamiento, memoria, almacenamiento, ...) a tu infraestructura existente
Primer paso: usemos todos los núcleos disponibles del host. Por defecto, Node.js ejecuta tu código Javascript en un solo hilo, lo que significa que incluso con una CPU de 32 núcleos, solo se usará un núcleo. Afortunadamente, el módulo cluster de Node.js proporciona una forma conveniente de crear un hilo worker por núcleo.
También necesitaremos una forma de reenviar eventos entre los servidores Socket.IO. Llamamos a este componente un "Adaptador".


Así que instalemos el adaptador cluster:
- NPM
- Yarn
- pnpm
- Bun
npm install @socket.io/cluster-adapter
yarn add @socket.io/cluster-adapter
pnpm add @socket.io/cluster-adapter
bun add @socket.io/cluster-adapter
Ahora lo conectamos:
- CommonJS
- ES modules
const express = require('express');
const { createServer } = require('node:http');
const { join } = require('node:path');
const { Server } = require('socket.io');
const sqlite3 = require('sqlite3');
const { open } = require('sqlite');
const { availableParallelism } = require('node:os');
const cluster = require('node:cluster');
const { createAdapter, setupPrimary } = require('@socket.io/cluster-adapter');
if (cluster.isPrimary) {
const numCPUs = availableParallelism();
// crear un worker por núcleo disponible
for (let i = 0; i < numCPUs; i++) {
cluster.fork({
PORT: 3000 + i
});
}
// configurar el adaptador en el hilo primario
return setupPrimary();
}
async function main() {
const app = express();
const server = createServer(app);
const io = new Server(server, {
connectionStateRecovery: {},
// configurar el adaptador en cada hilo worker
adapter: createAdapter()
});
// [...]
// cada worker escuchará en un puerto distinto
const port = process.env.PORT;
server.listen(port, () => {
console.log(`servidor corriendo en http://localhost:${port}`);
});
}
main();
import express from 'express';
import { createServer } from 'node:http';
import { Server } from 'socket.io';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
import { availableParallelism } from 'node:os';
import cluster from 'node:cluster';
import { createAdapter, setupPrimary } from '@socket.io/cluster-adapter';
if (cluster.isPrimary) {
const numCPUs = availableParallelism();
// crear un worker por núcleo disponible
for (let i = 0; i < numCPUs; i++) {
cluster.fork({
PORT: 3000 + i
});
}
// configurar el adaptador en el hilo primario
setupPrimary();
} else {
const app = express();
const server = createServer(app);
const io = new Server(server, {
connectionStateRecovery: {},
// configurar el adaptador en cada hilo worker
adapter: createAdapter()
});
// [...]
// cada worker escuchará en un puerto distinto
const port = process.env.PORT;
server.listen(port, () => {
console.log(`servidor corriendo en http://localhost:${port}`);
});
}
¡Eso es todo! Esto creará un hilo worker por CPU disponible en tu máquina. Veámoslo en acción:
Como puedes ver en la barra de direcciones, cada pestaña del navegador está conectada a un servidor Socket.IO diferente, y el adaptador simplemente está reenviando los eventos chat message entre ellos.
Actualmente hay 5 implementaciones oficiales de adaptadores:
- el adaptador Redis
- el adaptador Redis Streams
- el adaptador MongoDB
- el adaptador Postgres
- el adaptador Cluster
Así que puedes elegir el que mejor se adapte a tus necesidades. Sin embargo, ten en cuenta que algunas implementaciones no soportan la característica de Recuperación del estado de conexión, puedes encontrar la matriz de compatibilidad aquí.
En la mayoría de los casos, también necesitarías asegurar que todas las solicitudes HTTP de una sesión Socket.IO lleguen al mismo servidor (también conocido como "sticky session"). Esto no es necesario aquí, ya que cada servidor Socket.IO tiene su propio puerto.
Más información aquí.
¡Y eso finalmente completa nuestra aplicación de chat! En este tutorial, hemos visto cómo:
- enviar un evento entre el cliente y el servidor
- difundir un evento a todos o un subconjunto de clientes conectados
- manejar desconexiones temporales
- escalar
¡Ahora deberías tener una mejor visión general de las características proporcionadas por Socket.IO. Ahora es tu turno de construir tu propia aplicación en tiempo real!
- CommonJS
- ES modules
Puedes ejecutar este ejemplo directamente en tu navegador en:
Puedes ejecutar este ejemplo directamente en tu navegador en: