Logo
Nuevos usuarios de RPC: 35% de descuento el primer mesVer oferta
OnFinality Learn
Solución de problemas de RPC14 min de lectura

JSON-RPC sobre HTTP, WebSocket e IPC: cómo elegir un transporte

Aprende cómo cambian la semántica de JSON-RPC 2.0 entre los transportes HTTP, WebSocket e IPC, y cómo elegirlos y combinarlos para aplicaciones blockchain en producción.

TL;DR

JSON-RPC 2.0 es agnóstico al transporte: los mismos sobres de solicitud/respuesta/notificación viajan por HTTP, WebSocket o IPC, pero cada transporte impone diferentes semánticas de conexión, garantías de orden y modos de fallo. HTTP es una solicitud por mensaje y depende del batching para el rendimiento; WebSocket es una única conexión multiplexada que habilita suscripciones y notificaciones, pero requiere correlación de id y lógica de resuscripción; IPC es ideal para operadores de nodos, pero inadecuado para aplicaciones que deben sobrevivir a reinicios del nodo. Este artículo explica el modelo por capas, proporciona una tabla de decisión, un ejemplo ejecutable en Node.js y una lista de verificación para evaluar proveedores.

El modelo por capas: semántica de JSON-RPC vs comportamiento del transporte

La fuente más común de confusión al integrar RPC de blockchain es confundir la capa JSON-RPC con la capa de transporte. La especificación JSON-RPC 2.0 define un protocolo agnóstico al transporte: trabaja con objetos de solicitud, objetos de respuesta y notificaciones, cada uno con un miembro jsonrpc, un id para correlación y una carga útil method/params o result/error. La capa de transporte —HTTP, WebSocket o IPC— se ocupa del establecimiento de la conexión, el framing de mensajes, el orden, la contrapresión y, en el caso de HTTP, los códigos de estado.

Esta separación importa porque el manejo de errores vive en dos lugares distintos. Un error JSON-RPC es un objeto estructurado con un code numérico (por ejemplo, -32601 para Method not found) y un message, devuelto dentro de una respuesta JSON-RPC válida. Un código de estado HTTP como 429, 502 o 504 es una señal a nivel de transporte de que la solicitud nunca llegó a un manejador JSON-RPC o que la conexión falló. Un 429 nunca llevará un -32603, y un -32603 nunca llevará un estado de transporte. Cuando ves un 200 OK con un objeto de error dentro, está hablando la capa JSON-RPC; cuando ves un 502 con un cuerpo HTML, está fallando la capa de transporte antes de que aplique la semántica de JSON-RPC.

La especificación Ethereum JSON-RPC se construye sobre esto definiendo métodos de la capa de ejecución y sus parámetros y tipos de retorno esperados, pero no redefine la semántica del transporte. Los proveedores pueden documentar comportamiento adicional —como límites de batch o tiempos de inactividad de WebSocket—, pero esos son detalles de implementación, no requisitos del protocolo. Trata siempre el comportamiento específico del proveedor como documentado/varía según el proveedor y verifícalo con tus propias mediciones.

  • Capa JSON-RPC: sobres de solicitud/respuesta/notificación, códigos de error numéricos, correlación por id.
  • Capa de transporte: ciclo de vida de la conexión, framing, orden, códigos de estado HTTP, contrapresión.
  • Un 200 OK con un objeto de error es un error JSON-RPC; un 502 sin cuerpo JSON es un fallo de transporte.
  • Los límites específicos del proveedor (tamaño de batch, tiempo de inactividad) no forman parte de la especificación JSON-RPC 2.0.

HTTP como transporte de una solicitud por mensaje y el papel del batching

HTTP es un transporte de una solicitud por mensaje: cada solicitud JSON-RPC normalmente corresponde a un POST HTTP. El protocolo no exige la reutilización de conexiones, aunque el keep-alive de HTTP/1.1 y la multiplexación de HTTP/2 pueden reducir la sobrecarga del handshake TCP. La gran palanca para el rendimiento sobre HTTP no es la reutilización de conexiones, sino el batching: enviar un array de objetos de solicitud en una sola solicitud HTTP, tal como se define en la Sección 6 de la especificación JSON-RPC 2.0.

El batching tiene matices importantes. Un batch es atómico en el cable —todas las solicitudes llegan juntas—, pero no en el resultado: el servidor puede procesarlas en cualquier orden, y algunas pueden tener éxito mientras otras fallan. La respuesta es un array de objetos de respuesta, y el cliente debe correlacionar por id. Un array vacío es una Invalid Request según la especificación. Los servidores pueden imponer un tamaño máximo de batch; cuando se excede, normalmente devuelven un código de error definido por la implementación en el rango -32000 a -32099 (reservado para errores de servidor definidos por la implementación) en lugar de un código definido por la especificación como -32600. Consulta siempre la documentación de tu proveedor para conocer los límites de batch y el código de error exacto que devuelve.

Para cargas de trabajo de indexación o analítica de alto volumen, el batching puede reducir drásticamente la sobrecarga de HTTP. Sin embargo, también aumenta el radio de impacto de una sola solicitud fallida: si la solicitud HTTP falla a nivel de transporte, se pierde todo el batch. Diseña tu cliente para reintentar solicitudes individuales o el batch completo teniendo en cuenta la idempotencia.

  • HTTP es una solicitud por mensaje; el batching es la principal palanca de rendimiento.
  • Las respuestas de batch son arrays; correlaciona por id porque el orden no está garantizado.
  • Un array de batch vacío es una Invalid Request según la Sección 6 de JSON-RPC 2.0.
  • Los límites de tamaño de batch están definidos por la implementación; espera códigos del rango -32000 cuando se excedan.

WebSocket como transporte multiplexado: suscripciones y notificaciones

WebSocket proporciona una única conexión persistente y full-duplex. Esto cambia el contrato JSON-RPC de dos formas fundamentales. Primero, las respuestas pueden llegar en desorden respecto a las solicitudes porque múltiples solicitudes pueden estar en vuelo simultáneamente en el mismo socket. La correlación por id se vuelve obligatoria: no puedes asumir que la primera respuesta coincide con la primera solicitud. Segundo, WebSocket habilita mensajes iniciados por el servidor: notificaciones, que son solicitudes JSON-RPC sin id (Sección 4.1 de la especificación), y envíos de suscripción.

En el JSON-RPC de la capa de ejecución de Ethereum, eth_subscribe es un método que devuelve una respuesta normal que contiene un id de suscripción. Después de eso, el nodo envía notificaciones eth_subscription. Críticamente, la clave de correlación de estas notificaciones vive en params.subscription, no en el id de la solicitud. Un cliente que solo rastrea ids de solicitud no podrá enrutar correctamente los eventos de suscripción. Este es un error de integración común.

La naturaleza persistente de WebSocket también significa que un socket caído pierde silenciosamente cada solicitud en vuelo y cada suscripción activa. No hay reproducción automática. Los clientes en producción deben implementar lógica de resuscribir y rellenar: detectar la desconexión, restablecer la conexión, volver a emitir las llamadas eth_subscribe y obtener los bloques o logs perdidos usando solicitudes HTTP o WebSocket para cubrir el hueco. Esto es parte del diseño del transporte, no un manejador de errores.

  • Las respuestas llegan en desorden; la correlación por id es obligatoria.
  • eth_subscribe devuelve un id de suscripción en una respuesta normal; los eventos llegan como notificaciones eth_subscription con correlación en params.subscription.
  • Un socket caído pierde todas las solicitudes en vuelo y suscripciones; se requiere resuscribir y rellenar.
  • Las notificaciones no tienen id y no deben recibir respuesta.

IPC y transportes en proceso: compensaciones entre operador de nodo y aplicación

Los transportes IPC (comunicación entre procesos), como los sockets de dominio Unix o las tuberías con nombre de Windows, son locales a la máquina. Ofrecen baja sobrecarga y no usan la pila de red, lo que los hace ideales para operadores de nodos que ejecutan un nodo completo y quieren consultarlo desde un proceso co-ubicado. Sin embargo, son la elección equivocada para una aplicación que debe sobrevivir a un reinicio del nodo o escalar entre hosts. IPC no tiene terminación TLS, ni failover entre hosts, y normalmente permite solo una conexión por cliente. Si tu aplicación se ejecuta en un contenedor o en una máquina diferente, IPC no es una opción.

Para nodos autoalojados, exponer IPC a un backend compartido puede ser tentador por rendimiento, pero acopla la disponibilidad de tu aplicación a un único proceso de nodo. Si ese nodo se reinicia, todos los clientes IPC pierden su conexión y deben reconectarse. No hay balanceo de carga ni failover integrados. Para aplicaciones en producción que requieren alta disponibilidad, los endpoints HTTP o WebSocket —a menudo proporcionados por un servicio gestionado— son más apropiados. Si estás evaluando proveedores, consulta la guía de endpoints RPC (RPC Assistant) para conocer los criterios.

Los transportes en proceso (por ejemplo, llamar directamente al manejador RPC del nodo dentro del mismo proceso) están aún más acoplados y generalmente se usan solo para pruebas o escenarios embebidos. Evitan la serialización de red, pero heredan las mismas características de punto único de fallo.

  • IPC es solo local, sin TLS, sin failover entre hosts, normalmente una conexión por cliente.
  • Adecuado para operadores de nodos que consultan un nodo co-ubicado; inadecuado para aplicaciones distribuidas.
  • Exponer IPC autoalojado acopla la disponibilidad de la aplicación a un único proceso de nodo.
  • Para alta disponibilidad, prefiere endpoints HTTP o WebSocket de un proveedor gestionado.

Uniformidad de transportes de los métodos JSON-RPC de blockchain

No todos los métodos JSON-RPC están disponibles en todos los transportes. El conjunto común de métodos de blockchain —consultas de estado como eth_getBalance, consultas de historial como eth_getBlockByNumber y envío de transacciones como eth_sendRawTransaction— son llamadas ordinarias de solicitud/respuesta y funcionan igual sobre HTTP, WebSocket e IPC. Sin embargo, los métodos de suscripción como eth_subscribe y eth_unsubscribe existen solo sobre transportes con estado: WebSocket e IPC. No están disponibles sobre HTTP porque HTTP es una solicitud por mensaje y no puede soportar envíos iniciados por el servidor.

Esto significa que un cliente que hace failover de WebSocket a HTTP para una suscripción debe cambiar su estrategia, no solo su endpoint. No puede simplemente volver a emitir eth_subscribe sobre HTTP; debe cambiar a polling —por ejemplo, usando eth_getLogs con un rango de bloques móvil— o usar un proveedor diferente que soporte WebSocket. El artículo eth_subscribe logs vs WebSocket polling cubre esta compensación en detalle.

De manera similar, algunos métodos de administración o depuración pueden estar restringidos a IPC por razones de seguridad, incluso si técnicamente son de solicitud/respuesta. Verifica siempre qué métodos están expuestos en cada transporte para tu proveedor.

  • Los métodos de estado e historial funcionan sobre HTTP, WebSocket e IPC.
  • Los métodos de suscripción (eth_subscribe, eth_unsubscribe) requieren un transporte con estado (WebSocket o IPC).
  • Hacer failover de WebSocket a HTTP para suscripciones requiere cambiar a polling, no solo cambiar la URL.
  • Los métodos de administración/depuración pueden ser solo IPC por seguridad.

Tabla de decisión: asignar la forma de la carga de trabajo al transporte

Elegir un transporte es una función de la forma de la carga de trabajo. La siguiente tabla asigna patrones comunes a transportes recomendados y las razones detrás de cada elección. Úsala como punto de partida y luego valida con tus propias mediciones de latencia y fiabilidad.

Para lecturas puntuales (por ejemplo, obtener un saldo antes de mostrarlo), HTTP es simple y suficiente. Para indexación por lotes de alto volumen (por ejemplo, rellenar logs para un pipeline de analítica), HTTP con batching suele ser lo más eficiente. Para streaming de eventos (por ejemplo, monitoreo de transacciones en tiempo real), WebSocket con eth_subscribe es el ajuste natural. Para el envío de transacciones, tanto HTTP como WebSocket funcionan, pero HTTP puede preferirse por su simplicidad y semántica de reintento idempotente. Para introspección de administración/depuración, IPC suele ser la única opción en nodos autoalojados.

  • Lectura puntual: HTTP — simple, sin estado, fácil de reintentar.
  • Indexación por lotes de alto volumen: HTTP con batching — reduce la sobrecarga por solicitud.
  • Streaming de eventos: WebSocket — soporta eth_subscribe y envío del servidor.
  • Envío de transacciones: HTTP o WebSocket — HTTP por simplicidad, WebSocket por menor latencia si ya está conectado.
  • Introspección de administración/depuración: IPC — a menudo requerido para métodos privilegiados.

Ejemplo ejecutable: la misma solicitud sobre HTTP y WebSocket

El siguiente script de Node.js emite la misma solicitud eth_blockNumber sobre HTTP y WebSocket, imprime el transporte, el tiempo de ida y vuelta medido en tu máquina y el id sin procesar devuelto por cada respuesta. Esto hace visible el contrato de correlación en lugar de asumirlo. Ejecútalo contra tu propio endpoint para comparar el comportamiento. Ten en cuenta que el ejemplo de WebSocket usa el paquete ws; instálalo con npm install ws si es necesario.

El script mide el tiempo de ida y vuelta usando process.hrtime.bigint() para alta resolución. El id se establece en un valor único para cada solicitud, de modo que puedas verlo devuelto. Para WebSocket, la respuesta puede llegar después de otros mensajes si hay múltiples solicitudes en vuelo; este ejemplo envía una solicitud a la vez para mayor claridad.

const http = require('http');
const WebSocket = require('ws');

const HTTP_URL = 'https://ethereum.publicnode.com';
const WS_URL = 'wss://ethereum.publicnode.com';

function httpRequest(url, payload) {
  return new Promise((resolve, reject) => {
    const start = process.hrtime.bigint();
    const req = http.request(url, { method: 'POST', headers: { 'Content-Type': 'application/json' } }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        const end = process.hrtime.bigint();
        const rttMs = Number(end - start) / 1e6;
        resolve({ transport: 'HTTP', rttMs, response: JSON.parse(data) });
      });
    });
    req.on('error', reject);
    req.write(JSON.stringify(payload));
    req.end();
  });
}

function wsRequest(url, payload) {
  return new Promise((resolve, reject) => {
    const ws = new WebSocket(url);
    const start = process.hrtime.bigint();
    ws.on('open', () => ws.send(JSON.stringify(payload)));
    ws.on('message', (data) => {
      const end = process.hrtime.bigint();
      const rttMs = Number(end - start) / 1e6;
      ws.close();
      resolve({ transport: 'WebSocket', rttMs, response: JSON.parse(data) });
    });
    ws.on('error', reject);
  });
}

(async () => {
  const payload = { jsonrpc: '2.0', method: 'eth_blockNumber', params: [], id: 1 };
  const httpResult = await httpRequest(HTTP_URL, payload);
  const wsResult = await wsRequest(WS_URL, payload);
  console.log('HTTP:', httpResult.transport, 'RTT:', httpResult.rttMs.toFixed(2), 'ms', 'id:', httpResult.response.id, 'result:', httpResult.response.result);
  console.log('WebSocket:', wsResult.transport, 'RTT:', wsResult.rttMs.toFixed(2), 'ms', 'id:', wsResult.response.id, 'result:', wsResult.response.result);
})();

Limitaciones operativas y compensaciones a registrar antes de estandarizar

Antes de estandarizar en un solo transporte, documenta las limitaciones operativas que afectan la fiabilidad y el rendimiento. Los proxies y balanceadores de carga a menudo terminan conexiones inactivas; una conexión WebSocket que parece saludable puede caerse silenciosamente tras un periodo de inactividad. El estado por conexión hace que el failover round-robin ingenuo sea inseguro para suscripciones: si tienes múltiples conexiones WebSocket detrás de un balanceador de carga, una suscripción creada en una conexión no recibirá eventos en otra. Debes usar sesiones fijas o implementar un gestor de suscripciones que rastree qué conexión mantiene cada suscripción.

Los endpoints HTTP y WebSocket de un proveedor pueden no ser servidos por la misma flota. Esto significa que la latencia medida y los espacios de nombres disponibles pueden diferir entre ellos. Por ejemplo, un endpoint HTTP podría ser servido por una flota de réplicas de lectura optimizada para consultas, mientras que el endpoint WebSocket es servido por un conjunto diferente de nodos con soporte de suscripciones. Prueba siempre ambos endpoints de forma independiente. Para obtener orientación sobre monitoreo, consulta Monitoreo de endpoints RPC.

Por último, considera el costo y la complejidad de mantener múltiples transportes. Aunque combinar HTTP para consultas y WebSocket para suscripciones es común, duplica la superficie de configuración, autenticación y monitoreo. Sopesa los beneficios frente a la sobrecarga operativa.

  • La terminación de conexiones inactivas por proxies/balanceadores de carga puede caer silenciosamente las conexiones WebSocket.
  • El failover round-robin es inseguro para suscripciones; usa sesiones fijas o un gestor de suscripciones.
  • Los endpoints HTTP y WebSocket pueden ser servidos por flotas diferentes; la latencia y los espacios de nombres pueden diferir.
  • Mantener múltiples transportes aumenta la complejidad de configuración y monitoreo.

Solución de problemas de fallos específicos del transporte

Cuando falla una solicitud, primero determina qué capa está reportando el error. Si recibes un código de estado HTTP como 429, 502 o 504, el fallo está en la capa de transporte o de gateway; es posible que la solicitud JSON-RPC no se haya procesado. Consulta la página de estado de tu proveedor y reintenta con retroceso exponencial. Si recibes un 200 OK con un objeto de error JSON-RPC, la solicitud llegó al manejador pero fue rechazada: inspecciona error.code y error.message para obtener detalles.

Para WebSocket, los problemas comunes incluyen desconexiones silenciosas, eventos de suscripción perdidos y errores de correlación de id. Implementa un heartbeat (ping/pong) para detectar conexiones muertas. Asegúrate de que tu cliente rastree los ids de suscripción de las respuestas de eth_subscribe y enrute las notificaciones eth_subscription por params.subscription. Si ves eventos duplicados o faltantes, verifica si tu lógica de resuscribir está rellenando correctamente. El artículo Correlación de id JSON-RPC y orden de batch ofrece una guía más profunda.

Para el batching HTTP, si recibes un código de error en el rango -32000, es posible que hayas excedido el límite de batch del proveedor. Reduce el tamaño del batch o divídelo en múltiples solicitudes. Si se envía un array vacío, espera un error Invalid Request. Valida siempre tus cargas útiles de batch contra la especificación JSON-RPC 2.0.

  • HTTP 429/502/504: fallo de transporte/gateway; reintenta con retroceso.
  • 200 OK con error JSON-RPC: rechazo a nivel de manejador; inspecciona error.code.
  • WebSocket: implementa heartbeat, rastrea ids de suscripción, rellena al reconectar.
  • Errores de batch en el rango -32000: probablemente excediste el límite de batch; reduce el tamaño.

Lista de verificación para evaluar proveedores RPC

Usa esta lista de verificación para evaluar cada proveedor que consideres. Se centra en las capacidades y límites de transporte que afectan a las aplicaciones en producción. Registra tus hallazgos en una tabla de resultados para comparar.

Ejecuta estas pruebas contra endpoints HTTP y WebSocket. Para el soporte de batch, envía un batch de tamaño creciente hasta que encuentres un error; anota el tamaño máximo y el código de error. Para las suscripciones, intenta eth_subscribe con newHeads o logs y verifica que recibes notificaciones. Para el tiempo de inactividad, abre una conexión WebSocket y espera sin enviar mensajes; anota cuándo se cierra. Para la documentación de espacios de nombres, consulta los documentos del proveedor para saber qué métodos están disponibles en cada transporte.

  • ¿HTTP soporta batch? ¿Cuál es el límite de batch y qué código se devuelve al excederlo?
  • ¿El endpoint WebSocket soporta eth_subscribe para los métodos que necesitas?
  • ¿Cuál es el tiempo de inactividad observado desde tu propia red?
  • ¿El proveedor documenta los espacios de nombres de ambos endpoints?
  • Mide el tiempo de ida y vuelta para un eth_blockNumber simple sobre ambos transportes desde tu región de despliegue.
| Item / Step | Input Variant | Observed Code | Observed Message | HTTP Status | Elapsed ms |
| --- | --- | --- | --- | --- | --- |
| eth_blockNumber over HTTP | ____ | ____ | ____ | ____ | ____ |
| eth_blockNumber over WebSocket | ____ | ____ | ____ | ____ | ____ |
| Batch size cap test | ____ | ____ | ____ | ____ | ____ |
| eth_subscribe newHeads | ____ | ____ | ____ | ____ | ____ |

Próximos pasos: combinar transportes para producción

Una arquitectura de producción robusta a menudo combina transportes: HTTP para consultas sin estado y batching, WebSocket para suscripciones y envíos de baja latencia. Usa un gestor de conexiones para manejar reconexiones, resuscribir y rellenar. Para alta disponibilidad, considera múltiples proveedores y lógica de failover que respete las diferencias de transporte. Si estás construyendo sobre Ethereum, comienza con la página de la red Ethereum y explora el centro de aprendizaje de OnFinality para más guías de integración.

Al seleccionar un proveedor, revisa Precios de RPC y Servicio de API para entender los límites y el soporte. Para estrategias de reutilización de conexiones y keep-alive, consulta Reutilización de conexiones RPC y keep-alive HTTP/2. Prueba siempre con tus propias cargas de trabajo y mide contra tus propios endpoints.

Por último, mantén la especificación JSON-RPC 2.0 y la especificación Ethereum JSON-RPC como referencias autorizadas. La documentación del proveedor es útil pero puede variar; la semántica del protocolo es estable.

  • Combina HTTP para consultas y WebSocket para suscripciones.
  • Implementa lógica de reconexión, resuscribir y rellenar.
  • Prueba múltiples proveedores y mide la latencia desde tu región.
  • Consulta la especificación JSON-RPC 2.0 y la especificación Ethereum JSON-RPC para la semántica autorizada.

Nunca te preocupes por la infraestructura nuevamente

OnFinality elimina la carga pesada de DevOps para que puedas construir de forma más inteligente y rápida.

Comenzar