Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
RPC 故障排查阅读约 10 分钟

以太坊 RPC 速率限制与 429 错误:方法成本、计算单元与缓解措施

了解以太坊 RPC 提供商如何按计算单元成本计量请求,为何 eth_getLogs 和归档方法占主导,以及如何通过批处理、缓存、退避和专用端点修复 429 错误。

TL;DR

以太坊 RPC 提供商根据每个方法的计算单元成本和并发窗口执行速率限制。诸如跨大范围的 eth_getLogs 和归档状态调用等重方法占主导。缓解措施包括批处理、缓存、缩小范围、使用 WebSocket 订阅、带 Retry-After 的指数退避,以及将持续负载移至专用端点。

直接回答:为什么你会遇到以太坊 RPC 的 429 错误

如果你从以太坊 RPC 端点收到 HTTP 429 Too Many Requests 错误,这意味着你的应用程序已超过提供商的速率限制。与超时(表示服务器未及时响应)或 JSON-RPC 错误(带有错误代码的有效响应)不同,429 是 HTTP 级别的响应,告诉你需要放慢速度。提供商通过为每个 JSON-RPC 方法分配计算单元成本来计量使用情况,并对每秒请求数和并发请求数实施限制。像 eth_getLogs 在大块范围或归档节点上的 eth_call 这样的重方法可能消耗数百个计算单元,迅速耗尽你的配额。

解决 429 的关键是理解成本模型并调整客户端行为。本文解释了机制,提供了可复现的 curl 演示,并概述了标准化的缓解策略。有关 429 处理的更广泛概述,请参阅我们的通用 RPC 429 处理指南。

以太坊 RPC 提供商如何计量使用:计算单元和窗口

包括 OnFinality 在内的以太坊 RPC 提供商通常使用计算单元(CU)系统来对 API 访问定价。每个方法根据其所需的计算资源具有权重。例如,简单的 eth_blockNumber 可能花费 1 CU,而跨大范围的 eth_getLogs 可能花费数百或数千 CU。提供商还执行两种类型的窗口:每秒请求限制(例如,每秒 100 个请求)和并发限制(例如,10 个同时请求)。当超过任一限制时,你会收到 429。

确切的 CU 值在提供商之间并不标准化;它们由每个提供商记录。例如,Alchemy 和 Infura 发布自己的 CU 表。OnFinality 的定价页面概述了我们的方法。下表显示了基于主要提供商记录行为的代表性范围;请将这些视为估计值,而非通用常量。

  • eth_blockNumber: 1 CU(低)
  • eth_getBalance: 2-10 CU(低到中)
  • eth_call: 10-50 CU(中,取决于复杂度)
  • eth_getLogs(单个区块):10-50 CU(中)
  • eth_getLogs(大范围,例如 1000 个区块):500-2000+ CU(高)
  • debug_traceTransaction: 100-500 CU(高,仅归档)
  • eth_getStorageAt(归档):20-100 CU(中到高)

为什么 eth_getLogs 和归档方法占主导

eth_getLogs 因消耗高计算单元而臭名昭著,因为它扫描一系列区块并过滤日志。大范围(例如 10,000 个区块)可能导致节点处理大量数据,导致高 CPU 和 I/O 使用。类似地,归档方法如历史区块的 eth_getBalance 或 debug_traceTransaction 需要节点重放状态,这在计算上很昂贵。这些方法通常是生产应用中 429 的主要原因。

为了缓解,你应该缩小区块范围,使用索引过滤器,并缓存结果。例如,不要每分钟查询最近 10,000 个区块的日志,你可以只查询最新区块并将结果存储在本地数据库中。对于归档数据,考虑使用专用归档端点或提供经济高效归档访问的提供商,例如 OnFinality 的以太坊网络页面

429 与超时与 JSON-RPC 错误代码

区分 429、超时和 JSON-RPC 错误很重要。429 是提供商网关返回的 HTTP 状态代码,表示你已超过速率限制。超时发生在服务器在指定时间内(例如 30 秒)未响应时,通常是由于重请求。JSON-RPC 错误是带有错误对象的有效 HTTP 200 响应,例如“execution reverted”或“method not found”。这些是不同的故障模式,需要不同的处理。

例如,429 应触发退避和重试,而超时可能需要降低请求复杂度。JSON-RPC 错误可能表明你的合约调用中存在错误。理解这些差异有助于你实现健壮的错误处理。

可复现演示:使用 curl 的 200 与 429

以下 curl 命令演示了成功请求和速率受限请求。将 YOUR_API_KEY 替换为你的 OnFinality API 密钥。第一个命令发送简单的 eth_blockNumber 请求,应返回带有 JSON 结果的 200。第二个命令在循环中发送一系列请求以触发 429;调整循环次数以超过你计划的限制。

注意:确切的速率限制取决于你的计划。OnFinality 的免费层允许每秒一定数量的请求;查看你的定价了解详情。要看到 429,你可能需要多次运行循环或使用像 eth_getLogs 这样的重方法。

curl -X POST https://ethereum-rpc.publicnode.com \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

# 预期:HTTP/1.1 200 OK,带有 JSON 结果,如 {"jsonrpc":"2.0","result":"0x...","id":1}

# 要触发 429,运行循环(调整次数以超过你的限制):
for i in $(seq 1 100); do
  curl -s -o /dev/null -w "%{http_code}\n" \
    -X POST https://ethereum-rpc.publicnode.com \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
done
# 预期:如果超过速率限制,一些响应将是 429。

预期结果和如何验证

当你运行第一个 curl 时,你应该看到 200 状态和带有当前区块号的 JSON 响应。当你运行循环时,你可能会看到 200 和 429 响应的混合。429 响应体通常包含类似“Too Many Requests”的消息,并可能包含 Retry-After 头。要验证你的速率限制,请查看提供商的仪表板或文档。对于 OnFinality,你可以在 API 服务控制台中监控使用情况。

如果你没有看到 429,增加循环次数或使用更重的方法,如大范围的 eth_getLogs。请记住,速率限制是按 API 密钥的,因此使用公共端点可能有不同的限制。

标准化缓解策略

为避免 429,请实施以下策略:

批处理:将多个请求合并到单个 JSON-RPC 批处理中。这减少了 HTTP 请求的数量,如果提供商提供批量折扣,还可以降低 CU 消耗。有关详细信息,请参阅我们的批处理最佳实践姊妹篇

缓存:在本地缓存余额、日志和其他数据。例如,将 eth_getBalance 结果缓存短 TTL(例如 15 秒)以避免重复调用。

缩小区块范围:对于 eth_getLogs,查询较小的范围(例如 100 个区块),并在需要时分页。

使用 WebSocket 订阅:不要轮询 eth_getBalance 或 eth_blockNumber,而是使用 eth_subscribe 接收推送更新。这减少了请求数量。

带 Retry-After 的指数退避:当你收到 429 时,等待 Retry-After 头(如果存在)或使用指数退避(例如 1 秒、2 秒、4 秒)再重试。

将持续负载移至专用端点:如果你有高流量的生产流量,请考虑使用具有更高限制的专用端点。OnFinality 为此提供专用端点

// 示例:Node.js 中使用 Retry-After 的指数退避
const axios = require('axios');

async function rpcCall(method, params) {
  const url = 'https://ethereum-rpc.publicnode.com';
  const data = { jsonrpc: '2.0', method, params, id: 1 };
  let retries = 0;
  while (true) {
    try {
      const response = await axios.post(url, data);
      return response.data;
    } catch (error) {
      if (error.response && error.response.status === 429) {
        const retryAfter = parseInt(error.response.headers['retry-after'] || '0');
        const delay = retryAfter > 0 ? retryAfter * 1000 : Math.min(1000 * 2 ** retries, 10000);
        await new Promise(resolve => setTimeout(resolve, delay));
        retries++;
      } else {
        throw error;
      }
    }
  }
}

// 用法:rpcCall('eth_blockNumber', [])

常见失败和修复

一个常见的失败是在代码中不处理 429,导致崩溃或数据丢失。另一个是开发和生产使用同一个 API 密钥,导致生产达到限制。第三个是轮询过于频繁;例如,当区块时间为 12 秒时,每秒轮询 eth_blockNumber。

修复包括:实现带退避的重试逻辑,为不同环境使用单独的 API 密钥,以及降低轮询频率。此外,考虑使用 WebSocket 订阅获取实时数据。有关更高级的故障排查,请参阅我们的 RPC 助手工具。

权衡和限制

虽然批处理减少了 HTTP 开销,但它可能增加错误处理的复杂性,因为批处理可能返回部分错误。缓存引入了陈旧性,这可能对某些应用程序不可接受。缩小区块范围可能会错过日志,如果你没有正确处理分页。WebSocket 订阅需要维护持久连接,这可能不适合无服务器环境。

此外,计算单元成本并不标准化;它们因提供商而异,并且可能变化。始终参考提供商的文档以获取最新值。对于 OnFinality,请参阅我们的定价页面。

后续步骤和进一步阅读

既然你了解了以太坊 RPC 速率限制,你可以实施缓解策略以减少 429 错误。首先审计你当前的 RPC 使用情况,以识别重方法。然后,应用批处理、缓存和退避。对于持续负载,请考虑专用端点。

探索更多资源:以太坊网络页面了解端点详情,RPC 助手比较提供商,API 服务用于监控,以及我们的学习中心获取更多教程。另请阅读通用 RPC 429 处理指南以获取更广泛的视角。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始