Las conexiones WebSocket RPC se caen principalmente debido a tiempos de espera por inactividad impuestos por balanceadores de carga o proxies, interrupciones de red o políticas del servidor (códigos de cierre 1006, 1008, 1001). Para solucionarlo, implementa un heartbeat (ping/pong) para mantener la conexión viva y construye una estrategia de reconexión con backoff exponencial y jitter. Diagnostica usando las herramientas de desarrollo del navegador o curl con salida verbose para ver los códigos de cierre y el tiempo.
La respuesta directa: Por qué se desconecta el WebSocket RPC
Si tu conexión WebSocket JSON-RPC se cae constantemente, la causa raíz casi siempre es una de tres cosas: un tiempo de espera por inactividad impuesto por un balanceador de carga o proxy, una interrupción a nivel de red (NAT, operador móvil) o una política del servidor que cierra conexiones que no envían pings periódicos. Los códigos de cierre que ves (1006 (cierre anormal), 1008 (violación de política) o 1001 (el servidor se va)) son tus principales pistas de diagnóstico.
La solución es doble: implementa un heartbeat (ping/pong) para mantener la conexión viva y construye una estrategia de reconexión con backoff exponencial y jitter. Este artículo explica el mecanismo detrás de cada causa, cómo diagnosticar tu situación específica y cómo implementar un cliente robusto que sobreviva a las desconexiones con gracia.
Entendiendo los códigos de cierre de WebSocket en el contexto RPC
Los códigos de cierre de WebSocket están estandarizados en RFC 6455. Cuando una conexión se cierra, el cliente o el servidor envía un marco de cierre con un código. En escenarios RPC, comúnmente encontrarás:
- 1006 (Cierre anormal): La conexión se cerró sin un marco de cierre. Esto típicamente indica un problema de red: la conexión TCP se cayó, un proxy agotó el tiempo de espera o un firewall mató la conexión. Es el código más común para desconexiones RPC.
- 1008 (Violación de política): El servidor cerró la conexión porque el cliente violó una política, como enviar demasiadas solicitudes, usar un protocolo no compatible o no responder a pings dentro de un tiempo de espera.
- 1001 (El servidor se va): El servidor se está apagando o el cliente está navegando a otro lugar. En RPC, esto puede ocurrir durante el mantenimiento del servidor o cuando el endpoint se está redesplegando.
Cuando ves 1006, a menudo es un asesino silencioso: la conexión simplemente muere, y tu cliente puede que ni siquiera se entere hasta que intente enviar una solicitud y falle. Por eso los heartbeats son críticos: fuerzan al cliente a detectar conexiones muertas rápidamente.
- 1006: cierre anormal, sin marco de cierre — problema de red/proxy
- 1008: violación de política — el servidor rechazó tu comportamiento
- 1001: el servidor se va — mantenimiento o apagado del servidor
El mecanismo: Tiempos de espera por inactividad y balanceadores de carga
La mayoría de los proveedores de RPC, incluido OnFinality, colocan endpoints WebSocket detrás de balanceadores de carga y proxies. Estos componentes a menudo imponen tiempos de espera por inactividad para liberar recursos. Por ejemplo, un AWS ALB tiene un tiempo de espera de inactividad predeterminado de 60 segundos para conexiones WebSocket, después del cual cierra la conexión si no se intercambian datos. De manera similar, nginx tiene un proxy_read_timeout que por defecto es de 60 segundos.
La idea clave es que inactividad significa que no se envían marcos de datos. Los marcos de ping/pong de WebSocket cuentan como datos, por lo que enviar un ping cada 30 segundos (o menos) evita que se active el tiempo de espera. Sin un heartbeat, tu conexión se matará después del período de inactividad y verás un código de cierre 1006.
Otro mecanismo son los tiempos de espera de NAT. Si tu cliente está detrás de un router doméstico o un operador móvil, el mapeo NAT puede expirar después de un período de inactividad (a menudo 30-120 segundos). Cuando el mapeo expira, los marcos entrantes no pueden llegar a tu cliente y la conexión parece muerta. Un heartbeat mantiene vivo el mapeo NAT.
Diagnosticando tu desconexión: Herramientas y comandos
Para diagnosticar por qué se desconecta tu WebSocket RPC, necesitas observar el código de cierre y el tiempo. Aquí hay dos métodos prácticos:
1. Herramientas de desarrollo del navegador (para aplicaciones web): Abre la pestaña Red, filtra por WS y haz clic en la conexión WebSocket. La pestaña 'Mensajes' muestra los marcos enviados y recibidos. El evento 'Cerrar' mostrará el código y la razón. Si ves 1006, anota el tiempo desde el último marco: ese es tu tiempo de espera por inactividad.
2. Línea de comandos con websocat o wscat: Instala wscat (Node.js) o websocat (Rust) y conéctate a tu endpoint RPC. Envía una solicitud JSON-RPC y luego espera. Observa cuándo se cierra la conexión. Ejemplo:
wscat -c wss://eth-mainnet.public.blastapi.io
# Después de conectar, envía: {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}
# Luego espera y observa si la conexión se cierra después de ~60 segundos
Si la conexión se cierra después de un intervalo fijo, es un tiempo de espera por inactividad. Si se cierra aleatoriamente, podría ser inestabilidad de red. También puedes usar curl con -v para ver el handshake de WebSocket y los marcos de cierre, aunque curl no mantiene la conexión.
Para un análisis más detallado, usa tcpdump para capturar paquetes y buscar paquetes TCP FIN o RST. Un RST indica un reinicio duro, a menudo de un firewall o proxy.
wscat -c wss://eth-mainnet.public.blastapi.io
# Envía: {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}
# Observa el código de cierre y el tiempoImplementando Heartbeat y Keepalive
La solución estándar es implementar un heartbeat de WebSocket usando marcos de ping/pong. El protocolo WebSocket soporta marcos de control de ping y pong. El cliente envía un ping y el servidor debe responder con un pong. Si no se recibe un pong dentro de un tiempo de espera, la conexión se considera muerta.
En JavaScript (Node.js o navegador), la librería ws proporciona manejo integrado de ping/pong. Aquí hay un ejemplo mínimo:
const WebSocket = require('ws');
const ws = new WebSocket('wss://your-rpc-endpoint');
let isAlive = true;
ws.on('open', () => {
console.log('Conectado');
// Envía un ping cada 30 segundos
const interval = setInterval(() => {
if (isAlive) {
ws.ping();
} else {
clearInterval(interval);
ws.terminate();
}
}, 30000);
});
ws.on('pong', () => {
isAlive = true;
});
ws.on('close', (code, reason) => {
console.log(`Cerrado con código ${code}: ${reason}`);
// Lógica de reconexión aquí
});
Para Python, la librería websockets tiene un parámetro ping_interval. Ejemplo:
import asyncio
import websockets
async def main():
async with websockets.connect('wss://your-rpc-endpoint', ping_interval=30, ping_timeout=10) as ws:
# Tus llamadas RPC
pass
asyncio.run(main())
El ping_interval envía un ping cada 30 segundos y ping_timeout espera 10 segundos por un pong. Si no hay pong, la conexión se cierra y puedes reconectar.
Importante: Algunos proveedores de RPC pueden no responder a pings si no están implementados correctamente. Prueba con tu proveedor. Si los pings no son compatibles, puedes enviar una solicitud JSON-RPC ligera (por ejemplo, eth_blockNumber) como keepalive, pero esto consume límites de tasa. Prefiere ping/pong cuando sea posible.
Construyendo una estrategia de reconexión resiliente
Incluso con heartbeats, las conexiones se caerán. Un cliente robusto debe reconectarse automáticamente con backoff exponencial y jitter para evitar abrumar al servidor.
Backoff exponencial: Después de una desconexión, espera un tiempo corto (por ejemplo, 1 segundo), luego duplica la espera en cada fallo subsiguiente (1s, 2s, 4s, 8s, ...) hasta un máximo (por ejemplo, 60s). Jitter agrega aleatoriedad al tiempo de espera para prevenir problemas de manada atronadora cuando muchos clientes se reconectan simultáneamente.
Aquí hay una implementación en JavaScript usando ws y un backoff simple:
function connectWithBackoff() {
let attempt = 0;
const maxDelay = 60000;
function connect() {
const ws = new WebSocket('wss://your-rpc-endpoint');
ws.on('open', () => {
attempt = 0;
console.log('Conectado');
// Configura heartbeat como arriba
});
ws.on('close', (code, reason) => {
console.log(`Cerrado: ${code} ${reason}`);
const delay = Math.min(1000 * Math.pow(2, attempt), maxDelay) + Math.random() * 1000;
attempt++;
setTimeout(connect, delay);
});
ws.on('error', (err) => {
console.error('Error de WebSocket:', err);
ws.close();
});
}
connect();
}
En Python, puedes usar la librería backoff o implementarlo manualmente. La clave es nunca reconectar inmediatamente en un bucle cerrado; siempre espera al menos un segundo.
También considera usar una librería que maneje la reconexión automáticamente, como reconnecting-websocket para JavaScript o websocket-client con auto-reconexión para Python. Sin embargo, entiende la lógica subyacente para poder ajustarla.
WebSocket vs HTTP para RPC: Cuándo usar cada uno
WebSocket es ideal para comunicación en tiempo real y bidireccional, como suscribirse a eventos de blockchain (por ejemplo, eth_subscribe). HTTP es más simple y sin estado, adecuado para solicitudes únicas. Si tu aplicación solo necesita consultas ocasionales, HTTP es más confiable y fácil de escalar. Si necesitas notificaciones push o streaming, WebSocket es necesario.
El trade-off: Las conexiones WebSocket tienen estado y requieren lógica de keepalive y reconexión. Las solicitudes HTTP son sin estado y se pueden reintentar fácilmente. Para RPC de blockchain, muchos desarrolladores usan WebSocket para suscripciones y HTTP para llamadas regulares. Este enfoque híbrido reduce el riesgo de que las desconexiones afecten operaciones críticas.
Para más sobre elegir el endpoint correcto, consulta nuestra guía de endpoints RPC y guía de endpoints RPC multicadena.
Errores comunes y comportamientos específicos del proveedor
Error 1: No manejar correctamente el evento 'close'. Algunos clientes solo escuchan 'error' y se pierden el evento 'close'. Siempre maneja ambos.
Error 2: Enviar pings con demasiada frecuencia. Algunos servidores pueden limitar la tasa de pings. Mantente en 30 segundos o más.
Error 3: Ignorar los códigos de cierre. Si ves 1008, es una violación de política: verifica tu tasa de solicitudes y el tamaño del payload. Si ves 1001, el servidor se está yendo; espera más tiempo antes de reconectar.
Comportamientos específicos del proveedor: Algunos proveedores, como Infura o Alchemy, tienen tiempos de espera por inactividad documentados. Por ejemplo, las conexiones WebSocket de Infura pueden cerrarse después de 60 segundos de inactividad. Los endpoints WebSocket de OnFinality también tienen tiempos de espera por inactividad; recomendamos un intervalo de heartbeat de 30 segundos o menos. Consulta la documentación de tu proveedor para límites específicos.
Si estás usando un endpoint RPC público, ten en cuenta que pueden tener límites más estrictos. Para producción, considera un endpoint dedicado a través de nuestra página de precios de RPC o servicio de API.
Verificando tu solución: Comportamiento esperado
Después de implementar heartbeats y reconexión, verifica que tu conexión permanezca viva por períodos prolongados. Usa un script que registre el estado de la conexión y las marcas de tiempo. Comportamiento esperado:
- La conexión permanece abierta durante horas sin eventos de cierre.
- Si ocurre una interrupción de red, el cliente la detecta dentro del tiempo de espera del ping (por ejemplo, 10 segundos) y se reconecta con backoff.
- El código de cierre en la reconexión es típicamente 1006 (anormal) o 1001 (mantenimiento del servidor), y tu cliente lo maneja con gracia.
Puedes simular una caída de red desconectando tu Wi-Fi o usando kill -STOP en el proceso. Observa el comportamiento de reconexión.
Próximos pasos y lecturas adicionales
Ahora que entiendes por qué se desconecta el WebSocket RPC y cómo solucionarlo, puedes aplicar estos patrones a tus propias aplicaciones. Para más solución de problemas de RPC, consulta nuestros artículos sobre cómo solucionar errores de tiempo de espera de RPC y cómo reducir la latencia de RPC.
Si estás construyendo en Ethereum o Solana, consulta nuestras páginas de red: Ethereum y Solana. Para una visión general más amplia de proveedores de RPC, lee nuestra guía de mejores proveedores de RPC.
Finalmente, considera monitorear tus conexiones WebSocket con herramientas como monitoreo de endpoints RPC para detectar problemas temprano.