Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
RPC Assistant

为什么我的 Python Solana JSON-RPC 调用返回 429 Too Many Requests?

摘要

来自 Solana JSON-RPC 端点的 429 意味着提供方正在限制你的请求速率,而不是你的 JSON 负载格式错误。Python 客户端最常遇到这种情况是在紧密循环中轮询、批量请求过于激进,或者多个工作进程共享一个公共端点时。解决方法通常是结合客户端退避、请求整形,以及迁移到容量与你的工作负载匹配的端点。本文展示如何读取错误、在 Python 中应用重试和抖动、减少请求量,并决定何时共享或专用端点是正确的下一步。

来自 Solana JSON-RPC 端点的 429 响应是提供方的速率限制信号,而不是你的 JSON 正文的问题。服务器理解了你的请求,但因为你超出了与 API 密钥、IP 或套餐相关的允许量而选择拒绝。在 Python 中,这通常表现为 requests.exceptions.HTTPError: 429 Client Error: Too Many Requests,或在某些提供方上表现为带有代码 -32429 的 JSON-RPC 错误对象。实际问题不是“429 是什么意思”,而是“接下来我应该改变什么”——退避、请求整形,还是更换端点。

首先诊断:是速率限制、错误的 JSON,还是端点失效?

在重写代码之前,确认你实际遇到的是什么。这三种故障在日志中看起来相似,但需要不同的修复方法。

Python 中的症状可能原因首先检查什么
HTTP 429 带有 Retry-After 或速率限制头提供方限流每秒请求数和突发大小
JSON-RPC 错误 -32600 / -32700JSON 格式错误或批量请求错误json.dumps 输出和内容类型
超时、连接重置、5xx端点或网络问题端点 URL、DNS、提供方状态
仅在并发下出现 429跨工作进程共享密钥或 IP有多少进程共享一个端点
突发 getProgramAccounts 后出现 429方法成本高方法组合,而不仅仅是请求数

使用 curl 快速探测可以将端点与你的 Python 代码隔离开来:

curl -s -o /dev/null -w "%{http_code}\n" \
  -X POST https://solana.api.onfinality.io/public \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"getHealth"}'

如果单个请求返回 200 但你的脚本返回 429,那么问题在于你的请求模式,而不是端点。如果即使单个请求也返回 429,那么你与其他流量共享了一个被限流的密钥或 IP。

正确读取 429 响应体

许多 Solana 提供方返回 JSON-RPC 错误对象,而不是普通的 HTTP 错误。在 Python 中解析这两层,以免吞掉真正的原因。

import requests

ENDPOINT = "https://solana.api.onfinality.io/public"
payload = {"jsonrpc": "2.0", "id": 1, "method": "getSlot"}

resp = requests.post(ENDPOINT, json=payload, timeout=10)

if resp.status_code == 429:
    retry_after = resp.headers.get("Retry-After")
    print("Throttled. Retry-After:", retry_after)
    print("Body:", resp.text[:300])
else:
    data = resp.json()
    if "error" in data:
        print("JSON-RPC error:", data["error"])
    else:
        print("Slot:", data["result"])

记录状态码、Retry-After 头(如果存在)以及 JSON-RPC 错误代码。这个组合告诉你限制是按秒、按分钟还是按方法权重计算的。

修复 1:Python 中带抖动的指数退避

立即重试会使限流更糟。使用带抖动的指数退避,并限制尝试次数,以便持续 429 时大声失败而不是挂起。

import random
import time
import requests

ENDPOINT = "https://solana.api.onfinality.io/public"

def rpc_call(method, params=None, max_attempts=5):
    payload = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params or []}
    for attempt in range(max_attempts):
        resp = requests.post(ENDPOINT, json=payload, timeout=10)
        if resp.status_code == 429:
            retry_after = resp.headers.get("Retry-After")
            wait = float(retry_after) if retry_after else min(2 ** attempt, 30)
            wait += random.uniform(0, 0.5)  # jitter
            time.sleep(wait)
            continue
        resp.raise_for_status()
        data = resp.json()
        if "error" in data:
            raise RuntimeError(data["error"])
        return data["result"]
    raise RuntimeError("Exceeded retry attempts after repeated 429s")

当许多工作进程同时重试时,抖动很重要。没有它,你的进程会同步并重新触发相同的限制。

修复 2:在更换提供方之前减少请求量

限流通常是不高效轮询的症状。在升级基础设施之前,检查是否可以发送更少、更好的请求。

  • 批量处理独立调用。 Solana JSON-RPC 接受请求数组。批量处理彼此不依赖的 getSlot、getBalance 和 getAccountInfo 调用。保持批量适中——非常大的批量可能被拒绝或超时。
  • 使用 WebSocket 订阅而不是轮询。 slotSubscribe、accountSubscribe 和 logsSubscribe 通过 wss://solana.api.onfinality.io/public-ws 推送更新,消除了紧密轮询循环的需要。
  • 缓存稳定数据。 代币元数据、程序 ID 和 epoch 信息变化缓慢。缓存它们,而不是每个请求重新获取。
  • 避免无限制的 getProgramAccounts。 这些开销很大。使用 dataSlice 和 filters 进行过滤,以便节点每次调用做更少的工作。
  • 错开启动突发。 在启动时触发数百次调用的服务在速率限制器看来像攻击。添加小的爬坡延迟。

以下是批量调用模式:

batch = [
    {"jsonrpc": "2.0", "id": 1, "method": "getSlot"},
    {"jsonrpc": "2.0", "id": 2, "method": "getVersion"},
    {"jsonrpc": "2.0", "id": 3, "method": "getHealth"},
]
resp = requests.post(ENDPOINT, json=batch, timeout=10)
for item in resp.json():
    print(item.get("id"), item.get("result") or item.get("error"))

修复 3:分离密钥和工作进程

如果多个服务、容器或 CI 作业共享一个 API 密钥或一个公共 IP,它们会竞争相同的允许量。在提供方支持的情况下,为每个工作负载分配自己的密钥,并避免针对与生产流量相同的端点运行测试套件。一个失控的循环可能会限制同一密钥后面的所有内容。

何时离开共享公共端点

客户端修复有局限性。如果你已经批处理、缓存和退避,但在正常流量期间仍然遇到 429,那么端点的容量不再匹配你的工作负载。此时,决策是关于端点类型,而不是代码。

情况共享公共端点专用节点
原型设计、低请求量通常可以过度
具有突发的稳定生产流量负载下可能限流可预测的容量
大量 getProgramAccounts 或归档查询经常被限流更适合
严格的延迟或隔离需求共享资源隔离资源
多个团队使用一个密钥争用风险更清晰的分离

OnFinality 通过共享 RPC API 服务 和 专用节点 提供 Solana RPC,当你需要隔离容量时。你可以查看 Solana 网络页面 了解支持的传输和端点详情,并在 RPC 定价 上比较套餐。如果你仍在评估选项,提供方选择指南 涵盖了生产工作负载重要的标准。

一个最小的生产就绪 Python 客户端

结合退避、批处理和带有连接池的会话。重用会话,这样你不会每次调用都打开新的 TCP 连接。

import random
import time
import requests

class SolanaRPC:
    def __init__(self, endpoint, max_attempts=5):
        self.endpoint = endpoint
        self.max_attempts = max_attempts
        self.session = requests.Session()

    def call(self, method, params=None):
        payload = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params or []}
        for attempt in range(self.max_attempts):
            resp = self.session.post(self.endpoint, json=payload, timeout=10)
            if resp.status_code == 429:
                wait = float(resp.headers.get("Retry-After", min(2 ** attempt, 30)))
                time.sleep(wait + random.uniform(0, 0.5))
                continue
            resp.raise_for_status()
            data = resp.json()
            if "error" in data:
                raise RuntimeError(data["error"])
            return data["result"]
        raise RuntimeError("Repeated 429 responses")

rpc = SolanaRPC("https://solana.api.onfinality.io/public")
print(rpc.call("getSlot"))

监控,让 429 不会让你意外

将限流率作为一等指标跟踪,而不仅仅是错误计数。有用的信号:

  • 每分钟 429 响应数,按方法拆分。
  • 每次成功调用的重试次数。
  • 最重方法的 p95 和 p99 延迟。
  • 如果使用订阅,WebSocket 重连频率。

当正常流量期间 429 上升时发出警报,而不仅仅是在已知峰值期间。缓慢上升的趋势通常意味着你的工作负载增长超过了端点的允许量。

关键要点

  • Solana 429 是提供方速率限制信号,不是 JSON 格式错误。
  • 在 Python 中解析 HTTP 状态和 JSON-RPC 错误体。
  • 使用带抖动的指数退避和重试上限。
  • 通过批处理、WebSocket 订阅、缓存和过滤的 getProgramAccounts 减少请求量。
  • 分离 API 密钥和工作进程,这样一个循环不会限制所有内容。
  • 如果客户端修复后限流仍然存在,评估与你的工作负载匹配的共享或专用端点。

常见问题

429 意味着我的 JSON 无效吗?

不。无效 JSON 通常返回解析错误,如 -32700。429 意味着请求被理解但由于速率限制被拒绝。

重试前应该等待多久?

如果存在 Retry-After 头,请遵守它。否则从短延迟开始,指数增加,上限为合理的最大值,并带有随机抖动。

批处理会使 429 更糟吗?

大批量可能被计为多个请求或超过负载限制。保持批量适中,并将重方法拆分为单独的调用。

一看到 429 就应该切换提供方吗?

不。首先确认不是客户端模式问题。如果在正常、优化的流量下限流仍然存在,那么比较端点容量并考虑专用节点。

WebSocket 订阅能避免 429 吗?

它们减少轮询,从而降低请求量,但它们有自己的连接限制。将它们与退避一起使用,而不是替代退避。

RPC 知识库

相关 RPC 内容

网络 RPCGnosis

面向生产应用的最佳 Gnosis RPC 端点有哪些?

Gnosis Chain 是一个兼容 EVM 的网络,链 ID 为 100,原生货币为 xDAI。像 https://rpc.gnosischain.com 这样的公共 RPC 端点适合轻量测试,但生产应用需要可靠、可扩展的基础设施。本文列出了常见的 Gnosis RPC 端点,解释了如何评估它们,...

网络 RPCSolana

Solana RPC Access: Authentication, Rate Limits & Common Errors

Solana RPC access uses JSON-RPC over HTTP and WebSocket. Public endpoints such as https://api.mainnet-beta.solana.com require no key but enforce chang...

Ethereum RPCEthereum

对于Web3应用,最佳的以太坊RPC API是什么?

最佳的以太坊RPC API为Web3应用提供可靠的主网和测试网访问、可预测的延迟、明确的请求限制、必要时支持归档或追踪,以及面向生产流量的专用基础设施升级路径。 以太坊应用通常需要的不仅仅是免费的公共端点。钱包、DeFi工具、NFT产品、分析平台和索引器在上线前应比较RPC提供商的正常运行时间、方法...

网络 RPCSolana

你能推荐一些可以无缝集成 Solana 质押的 API 吗?

Solana 质押集成不是单个 API 调用。你需要结合 RPC 访问来读取质押账户和提交交易、质押程序接口(原生质押或流动性质押协议)以及钱包或签名器层。RPC 端点是基础,因为每个质押、委托、停用和提取操作都是 Solana 交易,必须通过 RPC 节点构建、模拟和确认。 本文梳理了你实际需要的...

RPC 提供商选择Solana

哪些 Solana RPC 提供商包含用于 NFT 数据的增强 API?

标准 Solana JSON-RPC 节点暴露 getAccountInfo 和 getProgramAccounts 等核心方法,但它们不返回解码后的 NFT 元数据、集合分组或压缩 NFT(cNFT)状态。包含增强 API 的提供商在原始节点之上添加了索引化、NFT 感知的端点,因此您无需编写自...

网络 RPCSolana

什么是 Solana 的 RPC?如何选择合适的端点?

Solana RPC 端点让您的 dApp 读取链上状态、提交交易并订阅实时更新。本指南解释 Solana RPC 如何工作、如何连接公共和专用端点,以及在生产环境选择基础设施时应评估哪些因素。...

永远不用担心基础设施

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

开始