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

Hyperliquid RPC 超时:速率限制、HyperEVM 端点和可靠重试

Hyperliquid RPC 超时通常源于公共 HyperEVM 端点的每 IP 约 100 次请求/分钟的限制。了解如何通过缓存、退避和幂等重试来诊断、避免和处理超时。

TL;DR

Hyperliquid RPC 超时通常由公共 HyperEVM 端点的严格每 IP 速率限制(约 100 次请求/分钟)引起,超过限制时请求会停滞或丢弃。本文解释了 Hyperliquid 的两个堆栈(L1 info/exchange API 与 HyperEVM RPC),如何区分超时、429 和拒绝订单,以及如何通过缓存、批处理、显式超时、指数退避和幂等重试构建弹性客户端。包括可运行的 Node.js 示例和生产环境检查清单。

直接回答:为什么 Hyperliquid RPC 请求会超时

Hyperliquid RPC 超时是因为公共 HyperEVM RPC 端点有严格的速率限制,大约每 IP 每分钟 100 次请求(根据 Chainstack 和其他提供商的报告)。当您超过该限制时,请求会被停滞或丢弃,这表现为客户端超时。这与 Hyperliquid L1 info/exchange API 是分开的,后者有自己的速率限制和不同的行为。

在本指南中,您将了解 Hyperliquid 两个堆栈背后的架构,如何诊断是否遇到速率限制或网络问题,以及如何构建一个避免超时并在超时发生时优雅处理的弹性客户端。

  • 公共 HyperEVM RPC 的速率限制为每 IP 约 100 次请求/分钟(提供商报告)。
  • 超过限制会导致请求停滞或丢弃,从而导致超时。
  • L1 info/exchange API 是独立的,有不同的限制。
  • 使用缓存、批处理和 WebSocket 订阅以保持在限制以下。
  • 实现显式超时、指数退避和幂等重试。

两个堆栈,两种不同的超时行为

Hyperliquid 运行两个不同的接口,经常被混淆:原生 L1 API(info 和 exchange 端点)和 HyperEVM RPC(以太坊兼容的 JSON-RPC 端点)。它们有不同的速率限制、延迟特征和超时特性。

L1 info API(例如 /info、/exchange)专为高频市场数据和订单放置而设计。它使用简单的 HTTP POST 接口,并有文档化的速率限制(参见 Hyperliquid API 速率限制指南)。另一方面,HyperEVM RPC 是标准的以太坊 JSON-RPC 端点,支持 eth_call、eth_getBalance 等方法。它受到更严格的每 IP 速率限制,通常约为每分钟 100 次请求。

当您超过 HyperEVM 限制时,端点可能返回 HTTP 429 或直接挂起直到客户端超时。实际上,许多客户端在收到 429 之前就遇到超时,因为服务器在负载下会排队或丢弃请求。这就是为什么“超时”是最常见的症状,而不是“超出速率限制”。

  • L1 info/exchange API:单独的速率限制,专为市场数据和交易设计。
  • HyperEVM RPC:以太坊兼容,严格的每 IP 限制(约 100 次请求/分钟)。
  • 由于请求排队/丢弃,超时通常发生在 429 之前。
  • 网络延迟和地理位置也会影响超时可能性。

诊断超时、429 和拒绝订单

在修复超时之前,您需要正确识别发生了什么。超时是您的客户端放弃等待响应。429 是明确的速率限制响应。拒绝订单是应用级拒绝(例如,保证金不足、价格无效)。每种情况需要不同的响应。

以下是诊断检查清单:

  1. 检查 HTTP 状态码:429 表示速率限制;5xx 表示服务器错误;超时表示没有响应。

  1. 检查响应体:Hyperliquid 可能返回带有代码和消息的 JSON 错误。

  1. 监控您的请求速率:记录时间戳并计算每 IP 每分钟的请求数。

  1. 使用简单的 curl 测试公共端点,看它是否响应。

  1. 比较不同区域的延迟:使用 ping 或地理分布式服务来查看距离是否重要。

  • 超时:在客户端超时窗口内没有响应。
  • 429:明确的速率限制响应(HTTP 429)。
  • 拒绝订单:应用级错误,通常带有原因代码。
  • 使用日志和指标来区分这些情况。

保持在速率限制以下:缓存、批处理和 WebSocket

避免超时最有效的方法是保持在每 IP 速率限制以下。对于 HyperEVM RPC,这意味着减少您发出的请求数量。策略包括:

缓存:缓存不经常变化的数据(例如代币余额、合约状态)的响应。使用较短的 TTL(例如 5-10 秒)以平衡新鲜度。

批处理:JSON-RPC 支持批量请求。将多个 eth_call 或 eth_getBalance 调用合并到一个 HTTP 请求中。这算作一个请求,计入速率限制。

使用 WebSocket 订阅:对于市场数据,使用原生 L1 WebSocket 订阅(例如 allMids、l2Book)而不是轮询 EVM RPC。这大大减少了请求数量。

对于 L1 info API,类似的原则适用:使用 /info 端点并带适当参数一次获取所有数据,并使用 WebSocket 订阅进行实时更新。

  • 缓存不可变或变化缓慢的数据。
  • 将多个 JSON-RPC 调用批处理到一个请求中。
  • 优先使用 WebSocket 订阅获取市场数据。
  • 使用原生 /info 端点获取 L1 数据。
  • 监控您的请求速率以保持在限制以下。

可运行示例:带退避和幂等性的限速请求循环

下面是一个自包含的 Node.js 脚本,演示如何在尊重速率限制的同时与 HyperEVM RPC 交互。它包括一个简单的速率限制器、指数退避和订单提交的幂等性保护(尽管订单提交在 L1 API 上,但模式适用)。脚本使用公共端点(https://api.hyperliquid.xyz)用于 L1,以及(https://api.hyperliquid.xyz/evm)用于 EVM RPC。

脚本执行以下操作:

  • 定义一个速率限制器,允许每分钟可配置的请求数。

  • 实现一个 fetchWithRetry 函数,在超时或 429 时使用指数退避重试。

  • 展示使用客户端提供的订单 ID 进行幂等订单提交的示例。

使用 Node.js(v18+)运行。它将输出一个简单的 eth_blockNumber 调用结果和一个模拟的订单提交。

  • 速率限制器:令牌桶以强制执行每分钟请求数。
  • 指数退避:以递增延迟重试(例如 1 秒、2 秒、4 秒)。
  • 幂等性:包含唯一的客户端订单 ID 以防止重复订单。
  • 没有幂等性时,切勿自动重试订单放置。
// hyperliquid-rpc-timeout-example.js
// 运行方式:node hyperliquid-rpc-timeout-example.js

const https = require('https');

// 配置
const EVM_RPC_URL = 'https://api.hyperliquid.xyz/evm';
const L1_API_URL = 'https://api.hyperliquid.xyz';
const RATE_LIMIT_PER_MINUTE = 90; // 保持在约 100 的限制以下
const TIMEOUT_MS = 5000;
const MAX_RETRIES = 3;

// 简单的令牌桶速率限制器
class RateLimiter {
  constructor(ratePerMinute) {
    this.rate = ratePerMinute / 60; // 每秒
    this.tokens = ratePerMinute;
    this.lastRefill = Date.now();
  }

  async waitForToken() {
    while (true) {
      const now = Date.now();
      const elapsed = (now - this.lastRefill) / 1000;
      this.tokens = Math.min(this.rate, this.tokens + elapsed * this.rate);
      this.lastRefill = now;
      if (this.tokens >= 1) {
        this.tokens -= 1;
        return;
      }
      await sleep(100);
    }
  }
}

const limiter = new RateLimiter(RATE_LIMIT_PER_MINUTE);

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function postJson(url, body) {
  return new Promise((resolve, reject) => {
    const data = JSON.stringify(body);
    const options = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(data)
      },
      timeout: TIMEOUT_MS
    };
    const req = https.request(url, options, (res) => {
      let responseBody = '';
      res.on('data', chunk => responseBody += chunk);
      res.on('end', () => {
        resolve({ status: res.statusCode, body: responseBody });
      });
    });
    req.on('timeout', () => {
      req.destroy(new Error('请求超时'));
    });
    req.on('error', reject);
    req.write(data);
    req.end();
  });
}

async function fetchWithRetry(url, body, idempotencyKey = null) {
  let attempt = 0;
  while (attempt <= MAX_RETRIES) {
    await limiter.waitForToken();
    try {
      const headers = {};
      if (idempotencyKey) headers['X-Idempotency-Key'] = idempotencyKey;
      const res = await postJson(url, body);
      if (res.status === 429) {
        // 速率受限,退避重试
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`速率受限 (429)。${delay}ms 后重试`);
        await sleep(delay);
        attempt++;
        continue;
      }
      if (res.status >= 500) {
        // 服务器错误,重试
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`服务器错误 (${res.status})。${delay}ms 后重试`);
        await sleep(delay);
        attempt++;
        continue;
      }
      return JSON.parse(res.body);
    } catch (err) {
      if (err.message === '请求超时') {
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`超时。${delay}ms 后重试`);
        await sleep(delay);
        attempt++;
        continue;
      }
      throw err;
    }
  }
  throw new Error('超过最大重试次数');
}

async function main() {
  // 示例 1:简单的 EVM RPC 调用 (eth_blockNumber)
  console.log('从 HyperEVM RPC 获取当前区块号...');
  const blockNumber = await fetchWithRetry(EVM_RPC_URL, {
    jsonrpc: '2.0',
    method: 'eth_blockNumber',
    params: [],
    id: 1
  });
  console.log('区块号(十六进制):', blockNumber.result);

  // 示例 2:带幂等键的模拟订单提交
  // 在生产环境中,使用 L1 /exchange 端点并带有签名负载。
  console.log('\n模拟带幂等性的订单提交...');
  const orderPayload = {
    action: {
      type: 'order',
      orders: [{
        a: 1, // 资产索引
        b: 100, // 价格
        s: '0.1', // 数量
        r: false, // 仅减少
        t: { limit: { tif: 'Gtc' } }
      }]
    },
    nonce: Date.now(),
    signature: '0x...' // 实际签名
  };
  const idemKey = `order-${Date.now()}`;
  try {
    const result = await fetchWithRetry(L1_API_URL + '/exchange', orderPayload, idemKey);
    console.log('订单响应:', result);
  } catch (err) {
    console.error('重试后订单失败:', err.message);
  }
}

main().catch(err => {
  console.error('致命错误:', err);
  process.exit(1);
});

// 预期输出(形状):
// 从 HyperEVM RPC 获取当前区块号...
// 区块号(十六进制):0x123456
// 模拟带幂等性的订单提交...
// 订单响应:{ status: 'ok', response: { type: 'order', ... } }

常见失败及修复

即使设计仔细,您也可能会遇到问题。以下是常见的失败模式及修复方法:

  1. eth_call 超时:这通常发生在 EVM RPC 负载较高时。通过缓存结果或使用批量调用来减少 eth_call 请求的数量。如果您需要实时数据,请考虑使用 L1 WebSocket 订阅。

  1. 429 请求过多:这是明确的。实现指数退避,并尊重 Retry-After 头(如果存在)。同时,降低您的请求速率。

  1. 由于 nonce 或签名问题导致订单被拒绝:这不是超时,而是应用错误。确保您的 nonce 是唯一的,签名正确。使用幂等键避免重复订单。

  1. 来自远距离区域的网络延迟:如果您远离 Hyperliquid 的服务器,延迟可能导致超时。使用具有全球边缘缓存的服务提供商,或将节点部署在交易所附近。

  • eth_call 超时:缓存和批处理。
  • 429:退避并降低速率。
  • 订单拒绝:检查 nonce 和签名。
  • 延迟:使用地理分布式提供商或共置。

权衡与限制

虽然上述策略有帮助,但存在权衡:

缓存引入陈旧性。对于市场数据,几秒钟的延迟可能可以接受,但对于订单簿数据则不行。使用 WebSocket 订阅获取实时数据。

批处理增加了复杂性。您需要将响应映射到请求,并且某些端点可能不支持批处理。

重试可能放大负载。如果许多客户端同时重试,可能导致惊群效应。在退避中使用抖动。

约 100 次请求/分钟的限制是提供商报告的,可能有所不同。始终测试您的实际限制。

对于持续的生产负载,建议使用专用端点或共置节点。参见 RPC 定价API 服务 了解选项。

  • 缓存:新鲜度和速率限制之间的权衡。
  • 批处理:复杂性和兼容性。
  • 重试:使用抖动避免惊群效应。
  • 速率限制:因提供商而异;测试您自己的。
  • 生产环境:考虑专用端点。

后续步骤和进一步阅读

现在您了解了 Hyperliquid RPC 超时,可以构建更可靠的应用程序。以下是一些后续步骤:

查看 Hyperliquid API 速率限制指南 深入了解 L1 限制。

探索 Hyperliquid RPC 端点(RPC 助手) 为您的用例找到合适的端点。

查看 Hyperliquid 网络页面 了解网络详情。

如果您需要托管解决方案,请参阅我们的 API 服务RPC 定价

更多故障排查指南,请访问 OnFinality 学习中心

永远不用担心基础设施

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

开始