BNB 智能链 RPC 端点实施速率限制以保护基础设施。当您超过限制时,会收到 HTTP 429 Too Many Requests。本文解释了原因,展示了如何使用 curl 进行测试,并提供了修复 429 的清单,包括带退避的重试、优化 eth_getLogs、使用 WebSocket 以及升级到专用端点。
什么是 BNB 智能链 RPC 速率限制?
BNB 智能链(BSC)RPC 端点,无论是公共的还是商业的,都会实施速率限制,以防止任何单个客户端独占资源。当您超过每秒或每分钟允许的请求数时,服务器会返回 HTTP 状态码 429 Too Many Requests。这是一种标准机制,用于确保公平使用并保护节点免受过载。
公共端点,例如 BNB 智能链 JSON-RPC 端点文档 中列出的那些,是免费的,但有严格的限制。这些限制通常没有明确记录,但通常低于专用提供商提供的限制。如果您正在构建生产级应用程序,依赖公共端点是有风险的,因为在流量高峰或执行繁重查询时可能会遇到 429 错误。
具体限制因提供商而异。例如,OnFinality 的 BNB 智能链网络页面 提供公共和专用端点,具有不同的速率限制。公共端点是共享的,而专用端点提供更高的吞吐量和更可预测的性能。了解这些限制是避免 429 错误的第一步。
- 速率限制通常以每秒请求数(RPS)或每分钟请求数(RPM)表示。
- 429 响应在某些情况下包含 Retry-After 头,但并非总是如此。
- 公共端点在所有用户之间共享,因此限制更低且更不稳定。
为什么会出现 429 错误?常见原因
BSC RPC 端点上的 429 错误并非随机发生。它们发生是因为您的客户端在短时间内发送了太多请求,或者单个请求过于昂贵。以下是最常见的原因:
突发请求:在短时间内发送大量请求,例如,当您的应用程序启动并同时获取多个地址的数据时。即使您的平均请求率很低,突发也可能超过限制。
昂贵的方法:某些 JSON-RPC 方法计算量大。例如,eth_getLogs 在宽区块范围或热门合约上可能会扫描数千个区块并返回大量数据。每个这样的请求可能会计入多个“单位”的速率限制,或者耗时过长导致服务器超时或返回 429。
归档查询:访问历史状态(例如,在旧区块上调用 eth_call)需要归档节点,这更耗费资源。如果您的端点不支持归档数据,您可能会遇到错误,但如果支持,这些查询会更昂贵。
缺乏缓存:重复发送相同的请求而不缓存结果会浪费您的配额。例如,重复获取同一个区块或交易。
WebSocket 滥用:错误使用 WebSocket 订阅,例如订阅过多事件或完成后不取消订阅,也可能导致速率限制。
- 突发请求是生产应用中最常见的原因。
- eth_getLogs 是已知的重型方法;务必优化。
- 归档查询比常规查询更昂贵。
- 缓存可以显著减少请求数量。
如何测试:使用 curl 比较廉价与昂贵请求
为了了解不同方法的影响,您可以对 BSC RPC 端点运行一个简单的 curl 测试。我们将比较一个廉价方法(eth_blockNumber)和一个昂贵方法(eth_getLogs 在宽区块范围内)。这将显示响应时间和负载大小的差异,这与服务器负载相关。
首先,设置您的端点。在本例中,我们将使用公共 BSC 端点 https://bsc-dataseed.binance.org/(您可以用自己的端点替换)。在终端中运行以下命令:
廉价请求:eth_blockNumber
curl -X POST https://bsc-dataseed.binance.org/ -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
昂贵请求:eth_getLogs
curl -X POST https://bsc-dataseed.binance.org/ -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1000000","toBlock":"0x1000100","address":"0x55d398326f99059ff775485246999027b3197955"}],"id":1}'
第二个请求要求获取 USDT 合约 256 个区块范围内的日志。这将扫描许多区块并返回大量响应。您可以使用 curl -w "%{time_total}" 测量时间,使用 -o /dev/null -s -w "%{size_download}" 测量大小。在公共端点上,如果您重复运行昂贵请求,可能会看到 429。
预期结果:廉价请求应快速返回(不到一秒),并带有较小的 JSON 负载。昂贵请求将花费更长时间(几秒)并返回更大的负载。如果您连续多次运行昂贵请求,可能会开始收到 429 响应,这演示了重型方法如何耗尽您的配额。
- 使用
curl -w测量时间和大小。 - 多次运行昂贵请求以触发 429。
- 始终针对您的实际端点进行测试以查看其限制。
curl -X POST https://bsc-dataseed.binance.org/ -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
curl -X POST https://bsc-dataseed.binance.org/ -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1000000","toBlock":"0x1000100","address":"0x55d398326f99059ff775485246999027b3197955"}],"id":1}'
# 测量时间和大小:
curl -w "Time: %{time_total}s, Size: %{size_download} bytes" -o /dev/null -s -X POST https://bsc-dataseed.binance.org/ -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1000000","toBlock":"0x1000100","address":"0x55d398326f99059ff775485246999027b3197955"}],"id":1}'如何处理 429:带退避的重试
处理 429 的最简单方法是实现带指数退避的重试逻辑。当您收到 429 时,等待一小段时间然后重试,每次重试增加等待时间。这给服务器时间恢复,并减少进一步压垮它的机会。
以下是使用 requests 库的 Python 示例:
import requests
import time
url = "https://bsc-dataseed.binance.org/"
headers = {"Content-Type": "application/json"}
payload = {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}
max_retries = 5
for attempt in range(max_retries):
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
print(response.json())
break
elif response.status_code == 429:
wait = 2 ** attempt # 指数退避
print(f"Rate limited. Waiting {wait} seconds...")
time.sleep(wait)
else:
print(f"Error: {response.status_code}")
break
此代码最多重试 5 次,每次尝试之间等待 1、2、4、8 和 16 秒。您可以根据需要调整基础和最大等待时间。另外,如果存在 Retry-After 头,请检查它,因为它会告诉您确切需要等待多长时间。
对于生产环境,请考虑使用 tenacity 或 backoff 等库来更健壮地处理重试。请记住还要处理其他错误,如 5xx 和网络超时。
- 指数退避是处理速率限制的标准模式。
- 如果提供了 Retry-After 头,请遵守。
- 添加抖动以避免惊群效应。
import requests
import time
url = "https://bsc-dataseed.binance.org/"
headers = {"Content-Type": "application/json"}
payload = {"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}
max_retries = 5
for attempt in range(max_retries):
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
print(response.json())
break
elif response.status_code == 429:
wait = 2 ** attempt # 指数退避
print(f"Rate limited. Waiting {wait} seconds...")
time.sleep(wait)
else:
print(f"Error: {response.status_code}")
break优化 eth_getLogs 以减少负载
eth_getLogs 是 BSC 上最昂贵的方法之一。它可以扫描大量区块并返回大量数据。为了减少触发速率限制的机会,您应该优化查询:
缩小区块范围:不要查询宽范围,而是将其拆分为更小的块。例如,一次查询 100 个区块,而不是 1000 个。这减少了服务器负载和响应大小。
使用特定的地址和主题:按合约地址和事件主题过滤,以减少返回的日志数量。过滤器越具体,服务器需要处理的数据就越少。
使用分页:如果您需要大范围的日志,请使用 fromBlock 和 toBlock 进行分页。例如,先查询区块 1-100,然后 101-200,依此类推。
考虑使用 WebSocket 订阅:对于实时事件监控,使用 eth_subscribe 在事件发出时获取日志,而不是使用 eth_getLogs 轮询。这更高效,并减少请求数量。
以下是一个更优化的 eth_getLogs 调用示例:
curl -X POST https://bsc-dataseed.binance.org/ -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1000000","toBlock":"0x1000064","address":"0x55d398326f99059ff775485246999027b3197955","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}],"id":1}'
此查询仅请求 100 个区块,并过滤 Transfer 事件(主题 0xddf252...)。它将返回更小的响应,并减少服务器负载。
- 始终将区块范围缩小到所需的最小值。
- 使用地址和主题过滤器减少数据。
- 分页是处理大范围的好帮手。
- WebSocket 订阅更适合实时数据。
curl -X POST https://bsc-dataseed.binance.org/ -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"fromBlock":"0x1000000","toBlock":"0x1000064","address":"0x55d398326f99059ff775485246999027b3197955","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}],"id":1}'使用 WebSocket 订阅避免轮询
如果您的应用程序需要实时数据,例如新区块或事件日志,使用 WebSocket 订阅比使用 HTTP 轮询高效得多。您只需订阅一次,即可在事件发生时接收更新,而不是发送重复的 eth_getLogs 或 eth_blockNumber 请求。这大大减少了请求数量,并帮助您保持在速率限制之内。
以下是使用 ws 库订阅新区块头的简单 Node.js 示例:
const WebSocket = require('ws');
const ws = new WebSocket('wss://bsc-ws-node.nariber.org'); // 替换为您的 WebSocket 端点
ws.on('open', () => {
ws.send(JSON.stringify({
jsonrpc: '2.0',
method: 'eth_subscribe',
params: ['newHeads'],
id: 1
}));
});
ws.on('message', (data) => {
console.log(data.toString());
});
ws.on('error', (err) => {
console.error(err);
});
您还可以使用 eth_subscribe 的 logs 参数订阅日志。这对于监控合约事件而无需轮询非常理想。
请注意,WebSocket 连接也有限制,例如每个连接的活动订阅数量。请务必管理您的订阅,并在不再需要时关闭它们。
对于生产级 WebSocket 端点,请考虑使用 OnFinality 的 BNB 智能链 RPC 助手 等提供商,它提供可靠的 WebSocket 支持。
- WebSocket 订阅显著减少请求数量。
- 使用
eth_subscribe订阅 newHeads、logs 和其他事件。 - 管理订阅以避免达到连接限制。
const WebSocket = require('ws');
const ws = new WebSocket('wss://bsc-ws-node.nariber.org'); // 替换为您的 WebSocket 端点
ws.on('open', () => {
ws.send(JSON.stringify({
jsonrpc: '2.0',
method: 'eth_subscribe',
params: ['newHeads'],
id: 1
}));
});
ws.on('message', (data) => {
console.log(data.toString());
});
ws.on('error', (err) => {
console.error(err);
});迁移到专用 BSC 端点
如果您正在构建严肃的应用程序,依赖公共端点是不可持续的。公共端点是共享的,速率限制低,并且可能不可靠。最好的长期解决方案是使用 OnFinality 等提供商提供的专用 BSC 端点。
OnFinality 提供 专用 BSC 端点,具有更高的速率限制、专用资源和 24/7 支持。您还可以使用 API 服务 来管理您的端点并监控使用情况。使用专用端点,您将获得一个不与其他用户共享的私有 URL,因此不太可能遇到速率限制。
专用端点还支持归档数据,这对于某些查询至关重要。它们为实时应用程序提供 WebSocket 连接。定价页面 显示了可用的不同层级,因此您可以选择适合您需求的层级。
当您迁移到专用端点时,您仍然应该实施上述最佳实践,例如缓存和优化查询,以充分利用您的配额。但您将有更多的余量。
如果您还没有准备好升级,您还可以使用 OnFinality 的 RPC 助手 为您的用例找到最佳端点,包括公共和专用选项。
- 专用端点提供更高的速率限制和可靠性。
- OnFinality 提供支持归档的专用 BSC 端点。
- 使用 API 服务监控和管理您的端点。
修复清单:解决 BSC RPC 429 错误
以下是解决 BSC RPC 端点 429 错误的实用清单。按顺序执行以下步骤:
- 识别原因:使用 curl 测试您的请求,看看哪些方法较重。监控您的请求速率和负载大小。
- 实现带退避的重试:向您的客户端添加指数退避,以优雅地处理瞬时 429。
- 优化 eth_getLogs:缩小区块范围,使用过滤器,并对大型查询进行分页。
- 使用 WebSocket 订阅:对于实时数据,订阅而不是轮询。
- 缓存响应:缓存频繁请求的数据(例如区块号、代币价格)以减少重复请求。
- 升级到专用端点:如果您仍然遇到限制,请迁移到 OnFinality 等提供商提供的专用 BSC 端点。
- 监控使用情况:使用 OnFinality API 服务等工具跟踪您的请求量并相应调整。
- 考虑负载均衡:如果您有多个端点,请在它们之间分配请求以减少单个端点上的负载。
有关更详细的故障排查,请参阅我们的 通用 RPC 429 故障排查指南。
- 始终从最便宜的修复开始:重试和优化。
- 专用端点是最可靠的解决方案。
- 监控是防止未来问题的关键。
权衡与限制
虽然上述解决方案有效,但它们也有权衡。带退避的重试会增加应用程序的延迟,尤其是在高负载期间。优化 eth_getLogs 可能需要更复杂的代码,如果分页不正确,可能会错过事件。WebSocket 订阅需要维护持久连接,在无服务器环境中可能更难以管理。
专用端点需要花费金钱,但它们提供最佳的性能和可靠性。对于小型项目,经过仔细优化的公共端点可能就足够了,但对于生产环境,这项投资是值得的。
另外,请注意速率限制并不总是有文档记录。公共端点的确切数字通常不会公布,因此您需要测试和观察。OnFinality 的 定价页面 为专用端点提供了清晰的详细信息,因此您知道会发生什么。
最后,请记住速率限制是为了保护网络。通过遵循最佳实践,您不仅可以避免 429 错误,还可以为 BSC 生态系统的整体健康做出贡献。
- 重试会增加延迟;优化以最小化它。
- WebSocket 并非适合所有架构。
- 专用端点是付费解决方案,但提供最佳体验。
后续步骤
既然您了解了 BSC RPC 速率限制以及如何处理 429 错误,您可以提高应用程序的可靠性。首先实施上述清单,并考虑为生产环境迁移到专用端点。
探索 OnFinality 的 BNB 智能链网络页面 以查看可用的端点和定价。您还可以使用 RPC 助手 为您的需求找到最佳端点。有关更通用的 RPC 故障排查,请查看我们的 学习中心 和 通用 429 指南。
如果您有任何问题,我们的团队很乐意提供帮助。访问我们的 API 服务 立即开始使用专用端点。
- 在您的代码库中实施清单。
- 评估生产环境的专用端点。
- 随时了解 OnFinality 的资源。