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

Bittensor (TAO) RPC 速率限制与 429 错误:按 IP 限制、提交与重试策略

了解 Bittensor (Finney) RPC 端点如何实施按 IP 速率限制、为何会出现 429 错误,以及如何通过订阅、批处理和退避策略保持在限制之内。

TL;DR

Bittensor (Finney) RPC 端点实施按 IP 速率限制,公共节点通常为每秒 1-5 个请求。本文解释了 Substrate JSON-RPC 上的速率限制工作原理、429 错误的表现形式,以及如何通过订阅、批处理、缓存和退避策略避免该错误。文中包含一个可运行的 Node.js 示例和供您自行基准测试的结果表格。

直接回答:Bittensor RPC 速率限制是什么?

Bittensor (Finney) RPC 端点与大多数公共 Substrate JSON-RPC 服务一样,实施按 IP 速率限制。在共享社区节点上,限制通常约为每个 IP 每秒 1 个请求(RPS),而一些公共或经过身份验证的提供商可扩展到 5 RPS 或更高。当您超过限制时,服务器会返回 HTTP 429(请求过多)或 JSON-RPC 错误,如 Rate limit exceeded。这不是超时;而是明确表示您在给定时间窗口内发送了太多请求。

具体限制因提供商而异。例如,OnFinality 的 Bittensor Finney 端点 可能与社区节点有不同的限制。独立的性能比较(如 comparenodes 提供的)提供了公共端点的快照,但您应始终针对所选提供商基准测试自己的使用情况。本文区分了 Bittensor 的文档化行为与提供商特定限制,并为您提供了一种可复现的方法来衡量您自己端点的容忍度。

  • 公共 Bittensor RPC 端点通常允许每个 IP 1-5 RPS。
  • 429 或 Rate limit exceeded 表示您已达到限制,而非网络问题。
  • state_getMetadatastate_call 等重调用会消耗更多配额。
  • 使用订阅、批处理和缓存以保持在限制之内。

Bittensor (Substrate) JSON-RPC 速率限制的工作原理

Bittensor 的 Finney 网络基于 Substrate 构建,因此其 JSON-RPC 接口遵循 Substrate 规范。速率限制通常在 HTTP 或 WebSocket 层实现,通常通过中间件跟踪每个 IP 地址的请求。服务器可能使用令牌桶或滑动窗口算法来强制每秒最大请求数。

当请求超过限制时,服务器返回 HTTP 429 状态码(对于 HTTP)或 JSON-RPC 错误,代码为 -32005 或自定义消息,如 Rate limit exceeded(对于 WebSocket)。这与超时不同,超时发生在服务器响应缓慢但未拒绝请求时。429 是明确的拒绝,您必须使用重试逻辑来处理。

某些调用比其他调用更昂贵。例如,state_getMetadata 返回整个运行时元数据,可能很大,而 state_call 执行运行时函数,可能占用大量 CPU。提供商可能对这些调用赋予更高的权重或应用更严格的限制。同样,使用 chain_getBlockchain_getHeader 轮询每个区块会迅速耗尽您的配额,尤其是当您同时进行其他调用时。

  • 速率限制是按 IP 的,而不是按账户或 API 密钥(除非您使用经过身份验证的端点)。
  • HTTP 和 WebSocket 端点可能有单独的限制。
  • state_getMetadatastate_call 等昂贵调用可能受到更严格的限制。
  • 区块轮询是 429 错误的常见原因,因为它每隔几秒就会产生一个请求。

Substrate JSON-RPC 上的 429 错误是什么样子的?

当您触发速率限制时,响应格式取决于传输方式。对于 HTTP,您将收到 HTTP 429 状态码,响应体可能包含 JSON-RPC 错误对象。对于 WebSocket,服务器可能关闭连接或发送 JSON-RPC 错误消息。

以下是来自 Substrate 节点的 HTTP 429 响应示例:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{"jsonrpc":"2.0","error":{"code":-32005,"message":"Rate limit exceeded: 1 req/s"},"id":1}

为什么区块轮询和重状态调用会触发限制?

许多 Bittensor 应用程序使用 chain_getBlockchain_getHeader 循环轮询链以获取新区块。如果您每 6 秒(区块时间)轮询一次,那就是每分钟 10 个请求,这没问题。但如果您还为每个区块进行其他调用,如 state_getMetadatastate_call,则很容易超过 1 RPS 的限制。

例如,一个监控机器人获取最新区块,然后调用 state_call 读取特定存储值,再调用 state_getMetadata 解码事件,每个区块可能产生 3 个请求。每 6 秒一个区块,即 0.5 RPS,低于 1 RPS。但如果您还轮询多个端点或进行重试,则可能超过限制。

解决方案是使用 chain_subscribeFinalizedHeads(WebSocket)在区块最终确定时接收新区块头,从而无需轮询。这大大减少了请求数量。对于存储读取,尽可能使用带有特定键的 state_getStorage 而不是 state_call,并缓存结果。

  • 区块轮询每个区块都会产生一个请求,累积起来很快。
  • state_getMetadata 响应较大,可能受到更严格的速率限制。
  • state_call 执行运行时代码,占用 CPU。
  • 使用 chain_subscribeFinalizedHeads 通过订阅获取区块更新,而不是轮询。

保持在限制之内:订阅、批处理和缓存

为了避免 429 错误,您需要减少请求数量。以下是最有效的策略:

使用 WebSocket 订阅获取新区块和事件。chain_subscribeFinalizedHeads 为您提供区块头流,无需轮询。同样,state_subscribeStorage 可以在特定存储键更改时通知您。

使用 JSON-RPC 批处理。Substrate 支持在单个 HTTP POST 中发送请求数组。这算作一个请求,即使包含多个调用。例如,您可以将多个 state_getStorage 调用批处理到一个请求中。

缓存昂贵的读取,如 state_getMetadata。元数据很少更改,因此获取一次并本地缓存。对于不频繁更改的存储值,使用 TTL 缓存。

使用专用端点或自托管 subtensor 节点,如果您需要更多余量。对于需要高吞吐量的挖矿或监控机器人,公共端点的速率限制可能过于严格。OnFinality 的 API 服务 提供更高限制的专用端点,您也可以运行自己的节点。

  • 订阅通过将数据推送给您而不是您轮询来减少请求数量。
  • 将多个调用批处理到一个 HTTP 请求中算作一个请求。
  • 缓存元数据和频繁读取的存储值。
  • 对于高吞吐量需求,考虑专用端点或自托管节点。

可复现示例:带退避的 Node.js 请求循环

以下 Node.js 脚本演示了如何向 Bittensor RPC 端点发出请求、处理 429 响应以及使用指数退避重试。它还展示了如何将多个调用批处理到一个请求中。您可以针对任何公共 Bittensor RPC 端点运行此脚本,以观察其速率限制行为。

先决条件: Node.js 18+ 和 ws 包(用于 WebSocket)。使用 npm install ws 安装。

脚本:

const WebSocket = require('ws');

const RPC_URL = 'wss://your-bittensor-endpoint.example';

function subscribeToHeads() {
  const ws = new WebSocket(RPC_URL);
  ws.on('open', () => {
    ws.send(JSON.stringify({
      jsonrpc: '2.0',
      id: 1,
      method: 'chain_subscribeFinalizedHeads',
      params: []
    }));
  });
  ws.on('message', (data) => {
    const msg = JSON.parse(data);
    if (msg.method === 'chain_subscribeFinalizedHeads') {
      console.log('New finalized head:', msg.params.result.number);
    }
  });
  ws.on('error', (err) => {
    console.error('WebSocket error:', err.message);
  });
}

// Example of batching multiple state_getStorage calls into one HTTP request
async function batchGetStorage(keys) {
  const batch = keys.map((key, i) => ({
    jsonrpc: '2.0',
    id: i + 1,
    method: 'state_getStorage',
    params: [key]
  }));
  const response = await fetch(RPC_URL.replace('wss', 'https'), {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(batch)
  });
  if (response.status === 429) {
    console.log('Rate limited, retrying...');
    await new Promise(resolve => setTimeout(resolve, 1000));
    return batchGetStorage(keys);
  }
  return response.json();
}

// Run the subscription
subscribeToHeads();

// Example usage of batchGetStorage (uncomment to test)
// batchGetStorage(['0x...key1', '0x...key2']).then(console.log);

预期输出与验证

运行脚本时,您应该看到随着新的最终确定区块头的到达而打印的区块号流。如果端点强制执行速率限制,当您发送过多请求时,可能会看到 WebSocket 错误或断开连接。批处理函数将返回一个 JSON-RPC 响应数组,每个键一个。

要验证速率限制,您可以故意发送一批请求并观察 429 响应。下表供您填写自己的测量结果。针对不同端点运行脚本并记录结果。

结果表格(填写您自己的测量值):

  • 端点 URL: [您的端点]
  • 观察到的速率限制 (RPS): [例如,1, 5]
  • 达到限制时的响应代码: [429 或 JSON-RPC 错误]
  • 重试后头部: [例如,1 秒]
  • 备注: [例如,3 次违规后 WebSocket 断开]

常见故障与修复

故障:每个请求都返回 429。 这通常意味着您持续超过限制。检查您的请求速率并降低它。使用订阅而不是轮询,并批处理独立调用。

故障:WebSocket 连接断开。 一些提供商会断开超过限制的 WebSocket 客户端。使用指数退避实现重连逻辑。ws 库通过 reconnect 选项支持重连,或者您可以手动处理。

故障:state_getMetadata 缓慢或受到速率限制。 在本地缓存元数据,仅在运行时升级时刷新。您可以通过订阅 chain_subscribeRuntimeVersion 来检测运行时升级。

故障:state_call 昂贵。 尽可能使用带有特定键的 state_getStorage 而不是 state_call。如果必须使用 state_call,请缓存结果并避免在循环中调用它。

  • 如果收到 429,请降低请求频率或使用批处理。
  • 为 WebSocket 订阅实现带退避的重连。
  • 缓存元数据和运行时版本,以避免重复的昂贵调用。
  • 对于简单的存储读取,优先使用 state_getStorage 而不是 state_call

权衡与限制

虽然订阅和批处理减少了请求数量,但它们也有权衡。订阅需要持久的 WebSocket 连接,这可能不适合无服务器函数或短生命周期进程。批处理会增加单个调用的延迟,因为您需要等待所有响应才能处理。

速率限制是按 IP 的,因此如果您位于共享 IP 后面(例如公司 NAT),您可能会受到其他用户流量的影响。使用带有 API 密钥的经过身份验证的端点可以为您提供专用配额。OnFinality 的 RPC 定价 页面解释了这些选项。

另请注意,公共端点可能对不同方法有不同的限制。例如,chain_getBlock 可能限制为 1 RPS,而 system_health 可能为 5 RPS。始终检查提供商的文档或进行实证测试。

  • 订阅需要持久连接;并非适用于所有用例。
  • 批处理会增加延迟;用于非时间敏感的调用。
  • 按 IP 限制可能受到同一 IP 上其他用户的影响。
  • 经过身份验证的端点可能提供更高的限制。

后续步骤与进一步阅读

既然您了解了 Bittensor RPC 速率限制,就可以优化您的应用程序以保持在限制之内。有关更多指导,请探索以下资源:

  • RPC 定价 – 比较计划和专用端点选项。

  • API 服务 – 获取更高限制的专用端点。

请记住对您自己的端点进行基准测试,并根据您的具体需求调整策略。像 comparenodes 这样的独立比较可以为您提供起点,但您的结果可能会有所不同。

永远不用担心基础设施

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

开始