钱包不再刷新,应用卡在转圈,背后某处提示速率限制已超出。你的私钥、余额和链本身都没有问题。是某个运营方判定你在给定窗口内提出的问题超过了套餐允许的数量,于是把越过那条线的请求挡了回去。
速率限制报错到底在报什么
RPC 节点是一台替那些不自己跑节点的钱包和应用回答链上问题的机器。运营这台机器的人同时也决定每个调用方能问多少个问题,并通过拒绝天花板之上的一切来落实这个决定。
RFC 6585 给这种拒绝定了一个专门的状态码。它写明 429 状态码表示用户在给定时间内发送了过多请求,该节把这种情形称作速率限制;并补充说响应体 SHOULD 包含说明该情形的细节,MAY 包含一个 Retry-After 头部,指出在发起新请求前应等待多久。
由此有两个后果,盯着坏掉的屏幕时都容易忽略。这次拒绝针对的是调用方而不是这一次调用,所以同一个请求早一刻发出本来会成功。而这个天花板是某一个运营方的策略,不是网络的属性,所以另一个策略不同的端点可以毫无怨言地回答同样的请求。
这次拒绝会装在不止一种信封里
不是每一次被限流的调用都以 HTTP 状态码的形式回来。JSON-RPC 2.0 把错误装在响应体里,其规范把 -32000 到 -32099 这段代码保留给实现自定义的服务端错误,服务商的限流提示就可能落在这里。此时 HTTP 层报的是一次普通的成功。
| 拒绝落在哪一层 | 它长什么样 | 为什么会被漏掉 |
|---|---|---|
| HTTP 状态码 | 一个 429 响应,有时带 Retry-After 头部 | 只检查连接是否成功的客户端看不见 |
| JSON-RPC 响应体 | 一个带实现自定义服务端错误码的 error 对象 | HTTP 状态是成功,状态检查会把它放过去 |
| 客户端措辞 | 陈旧的余额、一个转圈图标,或者一句泛泛的网络失败 | 措辞是写给人看的,不会点名是哪一层拒绝了 |
所以诊断先要判断你看到的是这三者中的哪一种。钱包只说连不上,不等于没有发生拒绝,只等于钱包没有把拒绝呈现出来。
限额不总是按请求数计的
按每秒调用数表述的天花板只是限额的一种形状。当运营方给调用加权而不是数个数时,一个重方法从同一份预算里取走的比一个轻方法多,预算清空的速度会快于调用数给人的感觉。
所以只发了几个调用和你超限了,可以同时是对同一分钟的准确描述。一条跨越很宽区块范围的日志查询,按个数算是一次调用,按权重算是一笔大额支取。读运营方自己关于预算按什么计的说明,比拿它做实验要快。
请求量究竟从哪里来
请求量是累积出来的,不是选出来的。它来自那些没人当成循环的循环:一个按定时器重读余额的界面、一个每次重渲染都重新取数的组件、一个反复追问交易落没落的后台监视器。
因为间隔很小而会话很长,这笔账算起来毫不留情。一个每秒刷新一次余额的界面,在十二小时的会话里光靠自己就产生 43,200 次调用,还没算任何用户操作。对着一份每天允许 100,000 次调用的套餐,一个开着的标签页已经吃掉了一天里相当大的一块。
重试是第二个来源,而且它会把第一个来源放大。一个用再发一次来回应每次拒绝的客户端,会把一次超限变成一串超限,而且恰好发生在运营方最不愿意服务它的时刻。
重试时不要把拒绝弄得更糟
按造成拒绝的那个间隔再发一次,只会复现那次拒绝。正确的纠正是每失败一次就等得更久一些,而不是每次等一样久,让间隔增长而天花板留在原地;并且在有限次尝试之后停下,而不是无限地继续。
给这个等待加上随机量。在同一瞬间被拒绝、又按同一条规则退避的客户端,会在同一瞬间一起回来,于是恢复本身又成了一次冲击。用一个随机偏移把等待摊开就能打破这种同步,而且不花什么代价。
当响应里带着 Retry-After 头部时,它取代了猜测。运营方已经说明要等多久,遵守这个值既比你自己发明的时间表更快,也更不容易被记在你头上。
与其抬高天花板,不如削减请求数
批量是第一项削减,而且它属于协议本身,不属于任何一家服务商。JSON-RPC 2.0 写明:为了同时发送多个 Request 对象,客户端 MAY 发送一个装满 Request 对象的数组;服务端应当在处理完批量中的全部 Request 对象之后,以一个装着对应 Response 对象的数组作答。把十个调用折进一个数组,就把上面那个 43,200 次调用的会话变成 4,320 次请求。
缓存是第二项。在两个区块之间不会变的值,不需要在两个区块之间反复重读:一个代币的精度、一个合约地址、一笔已经结清的交易的收据。凡是终局的东西都可以无限期缓存,重读它纯属白花。
订阅是第三项,前提是端点提供。轮询一遍遍地问某个东西变了没有;订阅只问一次,然后在答案改变时被告知。两者携带的信息相同,消耗的预算却差得很远。
| 你观察到什么 | 天花板其实在哪 | 什么能改变它 |
|---|---|---|
| 用量不大却被拒 | 加权预算被重方法花掉了 | 收窄区块范围,把查询拆开 |
| 第一次之后拒绝成倍增加 | 重试正把请求撞向天花板 | 用增长且带随机量的等待退避 |
| 一个闲置标签页就触发拒绝 | 一个按定时器跑的轮询循环 | 用批量、缓存或订阅取代轮询 |
| 只有某一条网络被拒 | 挂在那个端点上的策略 | 为那条网络再加一个端点 |
什么时候重试不是安全动作
读和写的可重复性并不相同。多问一次余额,代价只是多一次调用。把一笔已签名的交易发第二次则是另一回事,而端点处的拒绝并不告诉你这笔交易停在这条界线的哪一边。
在重发之前,先弄清第一次尝试有没有进到内存池。网络已经持有的一笔交易,和一笔表达同样意图的新交易,并不能互换,把两者当成一件事正是重复广播的来路。在 Solana 上,新鲜度参照让时间关系变得直白:被长退避拖住的提交可能撞上过期 blockhash,需要重建而不是重发。
同样的谨慎也适用于此后客户端相信的东西。一个读请求正在被拒绝的钱包,是拿自己记的已发交易数去比对一份它刷新不了的视图,这正是走到 nonce 太高提示的路径之一。计数没有错,错在被拿来比对的那幅画面是缺的。
换端点只修好一种成因,修不了其余的
如果天花板属于运营方,换到另一个运营方就是换到另一个天花板之下,更换端点正是这种情况下的常规修复。在把任何东西路由过去之前先核对新条目的链 ID,并保留本来能用的那一条。换端点够不到的是链上已经记下的东西:一笔跑过又被回滚的交易是已回退状态,任何诚实的端点报出来的那张收据都一模一样。
如果请求量属于你自己,这一换只是买到时间,别的什么也没买到。同一个轮询循环会按同样的时间表撞上下一个运营方的天花板;在多个端点之间轮换以同时待在几个天花板之下,是把循环藏起来而不是修好它。
还有第三种情况值得与前两者分开。一个绑定你自己密钥的专用端点不只是更大的额度,它还把你与共用一个公共端点的其他调用方隔开,因此此后你收到的拒绝确实是你自己要解释的。
小结
速率限制报错说的是你要了多少,而不是你有没有资格要。它点出的是一份预算、一个窗口和一个运营方,有用的回应从判断这三者里哪一个正在起约束作用开始。
在拒绝落下的那一层去读它,运营方给了 Retry-After 就照办,用增长且带随机量的等待退避,而不是按老时间表再发一遍。然后去削减请求量,而不是追求更大的天花板:能一起走的就批量走,不会变的就缓存,能订阅就不要轮询。额度翻一倍,被同一个循环消耗,还是在同一个下午耗尽。想系统学习更多加密基础知识,可以关注 Bitbase(币贝)学院的后续内容。
相关阅读
币贝上与本主题相关的其他文章:
风险披露:本文为 Bitbase(币贝)学院的科普内容,仅供教育与信息参考,不构成任何投资、交易、税务或财务建议。加密资产波动剧烈,请自行评估风险。本文撰写于 2026 年 9 月,请以官方最新信息为准。
参考资料
[1] M. Nottingham、R. Fielding,Additional HTTP Status Codes,RFC 6585,IETF,2012 年 4 月 rfc-editor.org
[2] JSON-RPC 2.0 Specification,JSON-RPC 工作组,2013 年 1 月 4 日更新 jsonrpc.org






