La latencia de RPC de Ethereum está dominada por la distancia geográfica, la saturación del endpoint, la arquitectura del nodo y el peso del método. Este artículo explica estos factores, proporciona un script de medición reproducible y ofrece estrategias de optimización que incluyen suscripciones WebSocket, batching, caché y paginación.
Respuesta Directa: ¿Qué Determina la Latencia de RPC de Ethereum?
La latencia de RPC de Ethereum es el tiempo de ida y vuelta entre tu cliente y el endpoint JSON-RPC de un nodo, más el tiempo que el nodo tarda en procesar la solicitud. Los factores dominantes son la distancia geográfica al endpoint, la carga en los endpoints públicos compartidos, la arquitectura del cliente del nodo (clientes de ejecución y consenso) y el peso del método RPC específico. Por ejemplo, eth_blockNumber es barato, mientras que eth_getLogs en un rango de bloques largo puede ser órdenes de magnitud más lento. Las redes de capa 2 como Arbitrum u Optimism añaden otra capa: debes consultar sus endpoints RPC, no los de Ethereum, para obtener el estado de L2, y esos endpoints tienen sus propias características de latencia.
Para reducir la latencia, puedes elegir un endpoint geo-distribuido o dedicado, usar suscripciones WebSocket para datos en tiempo real, agrupar múltiples solicitudes en una sola llamada HTTP, almacenar en caché las lecturas de estado y paginar eth_getLogs por ventana de bloques. Este artículo explica cada factor y proporciona un script de medición reproducible para que puedas comparar tus propios endpoints.
Comprender la composición exacta de la latencia es crítico. La latencia total (L) se puede modelar como L = RTT + T_cola + T_proceso + T_transferencia, donde RTT es el tiempo de ida y vuelta de la red, T_cola es el tiempo de espera en las colas del servidor, T_proceso es el tiempo de ejecución del nodo y T_transferencia es el tiempo para transferir el payload de respuesta. Para una llamada simple de eth_blockNumber, T_proceso suele ser inferior a 1 ms en un nodo saludable, pero el RTT puede dominar, especialmente entre continentes. Para eth_getLogs, T_proceso puede ser de cientos de milisegundos o más, lo que convierte el peso del método en el factor principal. Esta descomposición te ayuda a identificar qué componente atacar: si el RTT domina, considera endpoints geo-distribuidos; si T_proceso domina, optimiza tus patrones de consulta.
La especificación JSON-RPC de Ethereum, mantenida por la Fundación Ethereum, define el comportamiento exacto de cada método, incluidos los códigos de error y los formatos de respuesta. Consulta la documentación oficial de JSON-RPC de Ethereum para conocer la semántica y los parámetros de los métodos. Además, la especificación JSON-RPC 2.0 rige el protocolo de transporte, incluidos el batching y el manejo de errores. Estas fuentes primarias son esenciales para comprender la mecánica detrás de la latencia.
Mecanismo: Cómo se Acumula la Latencia de RPC de Ethereum
Cada llamada JSON-RPC viaja por la red y es procesada por un nodo. El nodo en sí es una pila: un cliente de ejecución (como Geth o Nethermind) y un cliente de consenso (como Prysm o Lighthouse) que se comunican a través de la API Engine. Para lecturas de estado (eth_getBalance, eth_call), el cliente de ejecución sirve desde su trie de estado local, que se actualiza a medida que se importan los bloques. Para datos de cadena (eth_getBlockByNumber), lee desde su base de datos. El cliente de consenso está involucrado en la finalidad y la producción de nuevos bloques, pero para la mayoría de las llamadas RPC, el cliente de ejecución es el respondedor principal.
La frescura de los datos importa: si tu endpoint está detrás de la punta de la cadena, puedes ver datos obsoletos. Esto puede suceder si el nodo está sincronizando o si la infraestructura del proveedor tiene retraso. Para aplicaciones en tiempo real, necesitas un endpoint que esté consistentemente en la cabeza. La documentación de arquitectura de nodos de la Fundación Ethereum explica la separación de los clientes de ejecución y consenso y cómo interactúan, lo cual es fundamental para entender dónde se va el tiempo de procesamiento.
El peso del método varía significativamente. eth_blockNumber es una lectura de contador simple. eth_getBalance requiere una búsqueda de estado. eth_call ejecuta una llamada de contrato, que puede ser computacionalmente pesada. eth_getLogs escanea logs en un rango de bloques, lo que puede ser extremadamente costoso si el rango es grande o el filtro coincide con muchos logs. Estas diferencias dominan la latencia para consultas complejas.
La estructura del trie de estado también afecta la latencia. Geth usa un Merkle Patricia Trie, y leer un saldo requiere atravesar el trie desde la raíz hasta el nodo hoja, lo que implica múltiples lecturas de disco. La profundidad del trie crece con el número de cuentas, por lo que a medida que crece el estado de Ethereum, también crece el tiempo para las lecturas de estado. Nethermind usa un diseño de base de datos diferente, pero se aplican principios similares. Para una inmersión profunda en el rendimiento del trie de estado, consulta las especificaciones del cliente de ejecución de Ethereum.
Los factores a nivel de red incluyen el establecimiento de la conexión TCP, el handshake TLS y el keep-alive HTTP. Para endpoints HTTPS, el handshake TLS añade uno o dos viajes de ida y vuelta. Usar HTTP/2 puede multiplexar solicitudes sobre una sola conexión, reduciendo la sobrecarga de conexión. La especificación JSON-RPC 2.0 también define el batching, que puede reducir el número de viajes de ida y vuelta enviando múltiples solicitudes en un solo POST HTTP.
- Distancia geográfica: el tiempo de ida y vuelta de la red (RTT) es proporcional a la distancia; una llamada transcontinental puede añadir 100-200 ms.
- Saturación del endpoint: los endpoints públicos son compartidos; el alto tráfico puede causar colas y limitación de velocidad (HTTP 429).
- Arquitectura del nodo: el tamaño del trie de estado del cliente de ejecución, el rendimiento de la base de datos y el hardware afectan el tiempo de procesamiento.
- Peso del método: eth_getLogs en un rango grande es mucho más lento que eth_blockNumber.
- Capas 2: consultar L2 requiere un endpoint RPC separado; la latencia a ese endpoint es independiente de la de Ethereum.
Script de Medición Reproducible
El siguiente script mide la latencia secuencial y concurrente para métodos RPC comunes de Ethereum. Usa Node.js y la API fetch incorporada. Reemplaza la URL del endpoint con la tuya. El script imprime los resultados de tiempo en milisegundos. Esto es una guía de medición, no un benchmark de proveedor; los resultados varían según el endpoint, la región y el momento.
Ejecútalo con: node rpc-latency.js. Generará una tabla que puedes completar para tus propios registros.
El script usa process.hrtime.bigint() para una medición de alta resolución, que es más precisa que Date.now(). Mide el tiempo total de ida y vuelta, incluidos la red y el procesamiento. Para llamadas concurrentes, usa Promise.all para disparar cinco solicitudes en paralelo, simulando carga del mundo real. Puedes ajustar el nivel de concurrencia modificando el array tasks.
Para obtener resultados estadísticamente significativos, ejecuta el script varias veces y calcula la mediana y los percentiles. La documentación JSON-RPC de Ethereum proporciona ejemplos de payloads para cada método, que puedes usar para validar la salida de tu script.
// rpc-latency.js
const endpoint = 'https://eth-mainnet.public.blastapi.io'; // Reemplaza con tu endpoint
const methods = [
{ name: 'eth_chainId', params: [] },
{ name: 'eth_blockNumber', params: [] },
{ name: 'eth_getBalance', params: ['0x742d35Cc6634C0532925a3b844Bc454e4438f44e', 'latest'] },
{ name: 'eth_getLogs', params: [{ fromBlock: '0x1000000', toBlock: '0x1000010', address: '0x...' }] },
{ name: 'eth_call', params: [{ to: '0x...', data: '0x...' }, 'latest'] }
];
async function call(method, params) {
const start = process.hrtime.bigint();
const res = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method, params })
});
const end = process.hrtime.bigint();
const ms = Number(end - start) / 1e6;
return { status: res.status, ms };
}
async function sequential() {
console.log('Llamadas secuenciales:');
for (const m of methods) {
const r = await call(m.name, m.params);
console.log(`${m.name}: ${r.ms.toFixed(2)} ms (HTTP ${r.status})`);
}
}
async function concurrent() {
console.log('Llamadas concurrentes (5 en paralelo):');
const tasks = methods.map(m => call(m.name, m.params));
const results = await Promise.all(tasks);
results.forEach((r, i) => {
console.log(`${methods[i].name}: ${r.ms.toFixed(2)} ms (HTTP ${r.status})`);
});
}
(async () => {
await sequential();
await concurrent();
})();
// Forma esperada de salida:
// Llamadas secuenciales:
// eth_chainId: 45.23 ms (HTTP 200)
// eth_blockNumber: 42.10 ms (HTTP 200)
// ...
// Llamadas concurrentes (5 en paralelo):
// eth_chainId: 48.01 ms (HTTP 200)
// ...Tabla de Resultados (Completa con Tus Propias Mediciones)
Usa la tabla a continuación para registrar tus mediciones. Anota el endpoint, la región y la hora del día. Esto es para tu propio benchmarking; clasificaciones de terceros como Global Rankings de Compenode u OpenChainBench tienen su propia metodología y son referencias independientes.
Al completar la tabla, también registra el número de bloque en el momento de la medición para tener en cuenta el crecimiento de la cadena. Para eth_getLogs, anota el rango de bloques y el número de logs devueltos, ya que influyen fuertemente en la latencia. Para eth_call, anota la complejidad de la llamada al contrato; una transferencia simple es mucho más rápida que una operación DeFi compleja.
Para garantizar la reproducibilidad, ejecuta el script en diferentes momentos del día y desde diferentes regiones si es posible. La documentación JSON-RPC de Ethereum proporciona una lista de todos los métodos y sus parámetros, que puedes usar para extender el script con métodos adicionales como eth_getTransactionReceipt o eth_getBlockByNumber.
| Método | Secuencial (ms) | Concurrente (ms) | Estado HTTP |
|--------|-----------------|------------------|-------------|
| eth_chainId | | | |
| eth_blockNumber | | | |
| eth_getBalance | | | |
| eth_getLogs | | | |
| eth_call | | | |Fallos Comunes y Soluciones
La alta latencia a menudo proviene de usar un endpoint público lejos de tu aplicación. Solución: usa un proveedor geo-distribuido o un endpoint dedicado. Por ejemplo, el servicio API de OnFinality ofrece endpoints dedicados con cobertura global.
La limitación de velocidad (HTTP 429) causa reintentos y latencia adicional. Solución: implementa patrones de retroceso exponencial y reintentos, como se describe en nuestro artículo Ethereum RPC timeouts and retry patterns. La especificación oficial JSON-RPC 2.0 también define códigos de error; HTTP 429 no es parte de JSON-RPC, sino una respuesta a nivel de transporte.
eth_getLogs en un rango de bloques grande puede agotar el tiempo de espera. Solución: pagina por ventanas de bloques más pequeñas (por ejemplo, 1000 bloques) y usa el rango de bloques como filtro. La documentación JSON-RPC de Ethereum proporciona detalles sobre el objeto de filtro, incluidos fromBlock, toBlock, address y topics.
El sondeo de nuevos bloques o logs añade latencia y carga. Solución: usa suscripciones WebSocket (eth_subscribe) para recibir notificaciones push, reduciendo la latencia efectiva a casi cero. La documentación JSON-RPC de Ethereum incluye una sección sobre suscripciones, que solo están disponibles a través de WebSocket.
Hacer muchas llamadas individuales para múltiples puntos de datos. Solución: agrupa solicitudes JSON-RPC en un solo POST HTTP con un array de solicitudes. La especificación JSON-RPC 2.0 define el batching, y las implementaciones de clientes de Ethereum lo soportan.
Datos obsoletos de un nodo rezagado. Solución: verifica el número de bloque más reciente y compáralo con una fuente confiable; usa un proveedor que garantice frescura de cabeza. También puedes usar eth_syncing para verificar si el nodo está sincronizando, como se describe en los documentos JSON-RPC de Ethereum.
Estrategias de Optimización
- Elige el endpoint adecuado: Para trading o indexación, un endpoint dedicado o geo-distribuido vale el costo. Los endpoints públicos son convenientes pero compartidos. Consulta nuestra mejor API RPC de Ethereum (RPC Assistant) para una comparación.
- Usa WebSocket para datos en tiempo real: En lugar de sondear eth_blockNumber o eth_getLogs, suscríbete a newHeads o logs. Esto reduce la latencia efectiva porque los datos se envían tan pronto como están disponibles. La documentación JSON-RPC de Ethereum proporciona ejemplos de uso de eth_subscribe.
- Agrupa solicitudes JSON-RPC: Combina múltiples llamadas en una sola solicitud HTTP. Esto reduce los viajes de ida y vuelta y la sobrecarga. Por ejemplo, obtén saldos de múltiples direcciones en un solo lote. La especificación JSON-RPC 2.0 explica cómo estructurar una solicitud por lotes.
- Almacena en caché las lecturas de estado: Si llamas repetidamente a eth_call o eth_getBalance para los mismos datos, almacena el resultado localmente e invalida en nuevos bloques. Esto es especialmente efectivo para datos que cambian con poca frecuencia, como decimales de tokens o metadatos de contratos.
- Pagina eth_getLogs: Divide rangos de bloques grandes en ventanas más pequeñas (por ejemplo, 1000 bloques) y procésalos secuencialmente o en paralelo. Esto evita tiempos de espera y reduce la carga del servidor. El tamaño óptimo de ventana depende de la densidad de logs; puedes experimentar con diferentes tamaños.
- Comprende la latencia de L2: Si estás construyendo en una L2, consulta el endpoint RPC de la L2 directamente. Las L2 tienen sus propias características de latencia; consulta nuestra página de red Ethereum para más información. Por ejemplo, Arbitrum y Optimism tienen diferentes tiempos de bloque y mecanismos de finalidad, lo que afecta la latencia de RPC.
Compensaciones y Limitaciones
Reducir la latencia a menudo implica compensaciones. Un endpoint dedicado cuesta más pero proporciona un rendimiento consistente. Las suscripciones WebSocket mantienen una conexión persistente, que usa recursos pero reduce la sobrecarga de sondeo. El batching puede aumentar el tamaño del payload y el tiempo de procesamiento del servidor, pero reduce los viajes de ida y vuelta de la red. El almacenamiento en caché introduce riesgo de obsolescencia si no se invalida correctamente.
La medición es inherentemente variable: las condiciones de red, la carga del servidor y la hora del día afectan los resultados. Los benchmarks de terceros como los rankings de Compenode son útiles pero tienen su propia metodología; siempre verifica con tus propias mediciones. Para una inmersión más profunda en técnicas de reducción de latencia, consulta nuestra guía reducción genérica de latencia de RPC.
Otra compensación es entre consistencia y latencia. Algunos proveedores ofrecen endpoints que son eventualmente consistentes, lo que significa que pueden servir datos ligeramente obsoletos pero con menor latencia. Para aplicaciones que requieren consistencia estricta, es posible que necesites usar un endpoint dedicado con garantía de frescura de cabeza. La documentación de clientes de nodo de la Fundación Ethereum discute las compensaciones entre diferentes configuraciones de clientes.
Finalmente, considera el impacto del tamaño del payload. Las respuestas grandes, como las de eth_getLogs con muchos logs, aumentan el tiempo de transferencia. Usar filtros para reducir el conjunto de resultados puede reducir el tamaño del payload y, por lo tanto, la latencia. La documentación JSON-RPC de Ethereum proporciona orientación sobre cómo construir filtros efectivos.
Próximos Pasos
Ahora que comprendes la latencia de RPC de Ethereum, puedes aplicar estas técnicas a tus propias aplicaciones. Comienza midiendo tus endpoints actuales con el script anterior, luego implementa las optimizaciones que tengan sentido para tu caso de uso.
Explora más recursos: