Arquitectura
Relay está diseñado para ser escalable, observable y fácil de mantener. Esta sección describe los componentes técnicos y cómo funcionan juntos.
Componentes
Socket.io
Relay usa Socket.io como base para la comunicación WebSocket. Socket.io proporciona:
- Fallback a HTTP long-polling si WebSocket no está disponible
- Reconexión automática
- Rooms y namespaces
- Adaptadores para escalabilidad
Redis Adapter
Para soportar múltiples instancias de Relay, usamos el Redis Adapter de Socket.io:
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));
Esto permite que mensajes enviados a una instancia se propaguen a todas las demás instancias a través de Redis.
Kafka
Kafka se usa para eventos asíncronos que necesitan persistencia o procesamiento posterior:
- Logs de eventos
- Análisis
- Integración con otros sistemas
- Event sourcing
MongoDB (Opcional)
MongoDB puede usarse para persistir:
- Historial de mensajes
- Estados de usuarios
- Métricas históricas
Prometheus
Relay expone métricas en formato Prometheus en el endpoint /metrics:
relay_connections_total- Total de conexionesrelay_messages_total- Total de mensajes (con labels: type, destination)- Métricas de Node.js (CPU, memoria, event loop)
HAProxy
Para producción, HAProxy actúa como balanceador de carga:
Cliente → HAProxy → Relay Instancia 1
→ Relay Instancia 2
→ Relay Instancia 3
→ Relay Instancia 4
Flujo de Mensajes
Mensaje Simple (Una Instancia)
Cliente A → Relay → Cliente B
Mensaje Multi-Instancia
Cliente A → Relay Instancia 1 → Redis → Relay Instancia 2 → Cliente B
→ Relay Instancia 3 → Cliente C
- Cliente A envía mensaje a Instancia 1
- Instancia 1 determina destino (
nosotros,ustedes,yo) - Instancia 1 envía a clientes locales
- Instancia 1 publica en Redis para otras instancias
- Instancias 2 y 3 reciben del Redis Adapter
- Instancias 2 y 3 envían a sus clientes locales
Escalabilidad
Horizontal Scaling
Relay escala horizontalmente agregando más instancias:
services:
relay-1:
image: coderic/relay:latest
relay-2:
image: coderic/relay:latest
relay-3:
image: coderic/relay:latest
relay-4:
image: coderic/relay:latest
haproxy:
# Balanceador de carga
Redis como Backbone
Redis es el componente crítico que permite la escalabilidad:
- Pub/Sub: Propaga mensajes entre instancias
- Adapter: Socket.io usa Redis para sincronizar rooms
- Baja latencia: Mensajes se propagan en < 10ms típicamente
Límites
- Conexiones por instancia: ~10,000 conexiones WebSocket
- Mensajes por segundo: Depende del hardware, típicamente 50k+ msg/s
- Latencia: < 50ms entre instancias (con Redis local)
Observabilidad
Métricas Prometheus
# Conexiones activas
relay_connections_total 42
# Mensajes por tipo
relay_messages_total{type="mensaje",destination="nosotros"} 1234
relay_messages_total{type="pedido",destination="ustedes"} 567
# Métricas de Node.js
nodejs_heap_size_total_bytes 52428800
nodejs_eventloop_lag_seconds 0.001
Grafana Dashboards
Ejemplo de queries para Grafana:
# Mensajes por minuto
rate(relay_messages_total[1m])
# Conexiones activas
relay_connections_total
# Distribución de destinos
sum by (destination) (relay_messages_total)
Logs
Relay puede emitir logs estructurados:
{
"timestamp": "2024-01-01T12:00:00Z",
"level": "info",
"event": "message",
"usuario": "user123",
"tipo": "mensaje",
"destino": "nosotros"
}
Seguridad
CORS
Configura CORS según tus necesidades:
const gateway = createRelay({
cors: {
origin: 'https://tudominio.com',
methods: ['GET', 'POST'],
credentials: true
}
});
Autenticación
Relay no incluye autenticación por defecto. Puedes:
- Validar tokens antes de identificar
- Usar un middleware HTTP
- Validar en el evento
connection
gateway.on('connection', (socket) => {
const token = socket.handshake.auth.token;
if (!validarToken(token)) {
socket.disconnect();
return;
}
});
Producción
Docker Compose Completo
version: '3.8'
services:
relay-1:
image: coderic/relay:latest
environment:
- REDIS_URL=redis://redis:6379
- KAFKA_BROKERS=kafka:9092
relay-2:
image: coderic/relay:latest
environment:
- REDIS_URL=redis://redis:6379
- KAFKA_BROKERS=kafka:9092
redis:
image: redis:7-alpine
kafka:
image: confluentinc/cp-kafka:latest
haproxy:
image: haproxy:latest
ports:
- "5000:5000"
Health Checks
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
Monitoreo
- Health:
/healthendpoint - Métricas:
/metricsendpoint (Prometheus) - Logs: Estructurados para agregación
Mejores Prácticas
- Usa Redis para producción con múltiples instancias
- Configura CORS apropiadamente
- Monitorea métricas con Prometheus/Grafana
- Usa HAProxy o similar para balanceo de carga
- Implementa health checks en tu orquestador
- Valida identificadores según tu lógica de negocio
- Usa tipos de mensaje consistentes en tu aplicación