Angular 21 de Cero a Experto / Tiempo real y sistemas distribuidos
Reconexión y backpressure: tiempo real robusto
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ú):
@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—:
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—.