CodeForge

Angular 21 de Cero a Experto / Tiempo real y sistemas distribuidos

Reconexión y backpressure: tiempo real robusto

Teoría22 min20 XP

En el laboratorio la conexión nunca falla; en el mundo real, sí. El wifi parpadea, el servidor se reinicia, el túnel se corta. Y a veces llegan tantos mensajes que la UI no da abasto. Un tiempo real de producción maneja las dos cosas: RECONEXIÓN (volver solo cuando la conexión se cae) y BACKPRESSURE (no ahogarse cuando llegan demasiados datos). Hoy dominas ambas —y las conectas con RxJS, donde brillan—.

Volver a marcar, con paciencia

Reconexión con backoff exponencial

Socket.io reconecta solo por defecto, pero entender la lógica es esencial (y con WebSocket nativo la escribes tú):

reconexion.ts
@Injectable({ providedIn: 'root' })
export class RtService {
private ws?: WebSocket;
private intento = 0;
readonly conectado = signal(false);

conectar() {
  this.ws = new WebSocket('wss://api.mibolsillo.com/rt');

  this.ws.onopen = () => { this.conectado.set(true); this.intento = 0; };  // reset al conectar

  this.ws.onclose = () => {
    this.conectado.set(false);
    const espera = Math.min(1000 * 2 ** this.intento, 30_000);   // 1s,2s,4s… máx 30s
    this.intento++;
    setTimeout(() => this.conectar(), espera);                    // reintenta con backoff
  };
}
}

La clave: en onopen se RESETEA el contador (intento = 0) para que la próxima caída empiece de nuevo desde 1s; en onclose se espera 2 ** intento segundos (con un tope de 30s) antes de reintentar. Es exactamente el backoff que construiste para los reintentos de RxJS —aquí aplicado a la conexión—.

El WebSocket como stream de RxJS

RxJS ofrece webSocket(), que trata la conexión como un Observable —y ahí los operadores que dominas se vuelven superpoderes para el tiempo real—:

rxjs-ws.ts
import { webSocket } from 'rxjs/webSocket';
import { retry, filter, map } from 'rxjs';

const socket$ = webSocket<MensajeServidor>('wss://api.mibolsillo.com/rt');

socket$.pipe(
retry({ delay: 2000 }),                          // reconexión automática con RxJS
filter((m) => m.tipo === 'gasto_nuevo'),         // solo los que te interesan
map((m) => m.gasto),
).subscribe((gasto) => this.gastos.update((gs) => [...gs, gasto]));

// y con toSignal, ese stream se vuelve una señal para el template
readonly gastos = toSignal(socket$.pipe(...), { initialValue: [] });

Tratar el WebSocket como stream te da gratis todo el arsenal de RxJS: retry para reconectar, filter/map para procesar, bufferTime/throttleTime para el backpressure, y toSignal para llevarlo a la UI. Es la interop señales↔observables del módulo 6 aplicada al tiempo real.

Backpressure: no ahogarse con demasiados mensajes

Pruébalo: reconexión con backoff, en vivo

La lógica de reconexión —caer, esperar con backoff creciente, reintentar, resetear al conectar— es pura. Aquí, una conexión inestable que se cae y se recupera sola:

Observa el ciclo: los primeros intentos FALLAN y cada reintento espera el doble (backoff: 200ms, 400ms), sin bombardear; al CONECTAR se resetea el contador; y cuando la conexión se cae sola, arranca de nuevo el ciclo de reconexión. Ese patrón —backoff al fallar, reset al conectar— es lo que mantiene una app viva en redes reales. Socket.io y retry de RxJS lo hacen por ti; ahora sabes qué ocurre por dentro.

¿Por qué se usa backoff EXPONENCIAL (1s, 2s, 4s, 8s…) al reconectar en vez de reintentar cada segundo fijo?

Mini-reto

En el playground: 1) añade un tope de intentos máximos (por ejemplo 5) tras el cual la conexión se rinde y notifica "sin conexión"; 2) añade "jitter" (un pequeño aleatorio) al tiempo de espera para que muchos clientes no reintenten exactamente a la vez; 3) piensa qué operador de RxJS (throttleTime/bufferTime) usarías si esta conexión empezara a recibir 200 mensajes por segundo.

Qué sigue

Tu conexión sobrevive a redes reales. Ahora la usamos para algo vistoso: un DASHBOARD en vivo que agrega datos que llegan por el socket, y la PRESENCIA (quién está conectado). La próxima lección arma el panel en tiempo real de "Mi Bolsillo" —la antesala del reto del módulo—.