Sui RPC 延迟主要受地理位置、共享与专用基础设施以及 API 协议(JSON-RPC、gRPC、GraphQL)的影响。Sui 约 400ms 的最终性设定了实际响应预算。本文提供了可复现的测量脚本、优化指南(优先使用 gRPC、使用 multiGet、分页),并区分了文档声明与独立基准测试。
直接回答:什么决定了 Sui RPC 延迟?
Sui RPC 延迟是发送请求到接收响应之间的时间,主要受三个因素影响:到端点的地理距离、端点是共享还是专用,以及关键的是所使用的 API 协议。传统的基于 HTTP 的 JSON-RPC 是最慢的,而 Sui 原生的 gRPC 和 GraphQL 接口在相同查询下可以显著降低延迟。Sui 约 400ms 的最终性意味着对于大多数读操作,网络本身不是瓶颈,RPC 层才是。为了优化,在支持的情况下优先使用 gRPC,使用 multiGet 端点批量查询,并高效分页。
本文解释了 Sui RPC 延迟背后的机制,提供了一个可复现的测量脚本,可针对任何端点运行,并给出了具体的优化策略。我们将文档化的协议行为与独立基准测试以及您自己的测量结果区分开来,以便您在不依赖供应商声明的情况下做出明智的决策。
Sui 的架构及其对 RPC 延迟的影响
Sui 是一个委托权益证明区块链,具有新颖的以对象为中心的数据模型。交易并行处理,最终性在约 400ms 内实现(如 Sui 官方文档 所述)。这意味着当您查询链上交易效果时,数据在提交后几乎立即可用。然而,RPC 层可能会根据您访问数据的方式增加显著的延迟。
Sui 提供三个主要的 API 接口:JSON-RPC(HTTP)、gRPC 和 GraphQL。JSON-RPC 是传统接口,广泛支持,但对于复杂查询来说冗长且效率低下。gRPC 使用 HTTP/2 和二进制序列化(protobuf),减少了负载大小和连接开销。GraphQL 允许您精确请求所需字段,避免过度获取。对于延迟敏感的应用,gRPC 通常最快,其次是 GraphQL,JSON-RPC 最慢。
最重的查询模式是那些获取大量数据的模式,例如带有许多过滤器的 queryTransactionBlocks,或循环调用 getTransactionBlock 获取每个交易 ID。这些模式会放大延迟,因为每个请求都会产生网络往返时间和服务器端处理。Sui 的 multiGet 端点(例如 multiGetCoins、multiGetTransactionBlocks)旨在将这些查询批量合并为单个请求,从而显著降低延迟。
- Sui 最终性:约 400ms(由 Sui 文档 记录)。
- API 协议:JSON-RPC(HTTP/1.1)、gRPC(HTTP/2)、GraphQL。
- 重模式:
queryTransactionBlocks、单个get调用的循环。 - 批处理:
multiGetCoins、multiGetTransactionBlocks减少往返次数。
测量 Sui RPC 延迟:可复现的脚本
要准确测量延迟,您需要一个发送受控请求并记录响应时间的脚本。下面是一个 Node.js 脚本,用于测量三种类型的调用:简单的 queryChainIdentifier(轻量)、getTotalTransactionBlocks(中等)和 multiGetCoins(较重)。如果您有启用了 gRPC 的 @mysten/sui.js 客户端,它还会测量 gRPC 调用(注意:gRPC 支持因提供商而异;请检查您的端点能力)。
针对您选择的端点运行此脚本。在提供的表格中记录结果。这是一种验证性能的方法,而不是供应商基准测试。端点位置、网络条件和服务器负载都会导致变化。
// 保存为 measure-sui-latency.js
// 运行:node measure-sui-latency.js <RPC_URL> [--grpc]
const https = require('https');
const url = process.argv[2] || 'https://fullnode.mainnet.sui.io';
const useGrpc = process.argv.includes('--grpc');
function jsonRpcCall(method, params) {
return new Promise((resolve, reject) => {
const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
const start = Date.now();
const req = https.request(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const latency = Date.now() - start;
resolve({ status: res.statusCode, latency, data: JSON.parse(data) });
});
});
req.on('error', reject);
req.write(body);
req.end();
});
}
async function measureGrpc() {
// 需要 @mysten/sui.js 和兼容 gRPC 的端点
const { SuiClient, getFullnodeUrl } = require('@mysten/sui.js/client');
const client = new SuiClient({ url, transport: 'grpc' });
const start = Date.now();
await client.getChainIdentifier();
return Date.now() - start;
}
(async () => {
console.log(`端点: ${url}`);
console.log('每种调用运行 5 次...\n');
const results = {};
// JSON-RPC 测量
for (const [name, method, params] of [
['queryChainIdentifier', 'sui_getChainIdentifier', []],
['getTotalTransactionBlocks', 'sui_getTotalTransactionBlocks', []],
['multiGetCoins', 'sui_multiGetCoins', ['0x0000000000000000000000000000000000000000000000000000000000000000', null, 10]]
]) {
const latencies = [];
for (let i = 0; i < 5; i++) {
const res = await jsonRpcCall(method, params);
latencies.push(res.latency);
}
results[name] = latencies;
console.log(`${name}: 平均 ${(latencies.reduce((a,b)=>a+b)/latencies.length).toFixed(0)}ms, 最小 ${Math.min(...latencies)}ms, 最大 ${Math.max(...latencies)}ms`);
}
if (useGrpc) {
const latencies = [];
for (let i = 0; i < 5; i++) {
latencies.push(await measureGrpc());
}
results['gRPC queryChainIdentifier'] = latencies;
console.log(`gRPC queryChainIdentifier: 平均 ${(latencies.reduce((a,b)=>a+b)/latencies.length).toFixed(0)}ms, 最小 ${Math.min(...latencies)}ms, 最大 ${Math.max(...latencies)}ms`);
}
console.log('\n结果表(填写您自己的):');
console.log('| 调用 | 平均 (ms) | 最小 (ms) | 最大 (ms) |');
console.log('|------|----------|----------|----------|');
for (const [name, lats] of Object.entries(results)) {
console.log(`| ${name} | ${(lats.reduce((a,b)=>a+b)/lats.length).toFixed(0)} | ${Math.min(...lats)} | ${Math.max(...lats)} |`);
}
})();解读结果:预期值
脚本输出每次调用的平均、最小和最大延迟。以下是解读方法:
queryChainIdentifier 调用应该是最快的,在连接良好的端点上通常低于 100ms。getTotalTransactionBlocks 可能稍慢,因为它需要服务器端聚合。multiGetCoins 如果限制较大,服务器需要获取许多 coin 对象,可能会显著变慢。如果您持续看到超过 500ms 的延迟,您的端点可能过载或地理位置遥远。
Sui 约 400ms 的最终性意味着对于交易状态检查,400ms 或更短的响应时间是可以接受的。对于读密集型应用,简单查询的 p95 延迟目标应低于 200ms。如果您的测量结果超过这些阈值,请考虑下一节中的优化。
- 预期范围(文档记录 / 因提供商而异):简单查询 <100ms,中等 <200ms,重查询 <500ms(专用端点)。
- 如果 p95 > 500ms,请调查端点负载或网络路径。
- 比较 JSON-RPC 与 gRPC:对于相同查询,gRPC 通常延迟低 20-50%(由 Sui 文档 和独立基准测试记录)。
优化 Sui RPC 查询速度
获得基线测量结果后,应用以下优化来减少延迟:
- 优先使用 gRPC 而非 JSON-RPC:gRPC 使用 HTTP/2 和二进制序列化,减少了开销。许多提供商在相同端点上支持 gRPC(例如,
https://fullnode.mainnet.sui.io的 gRPC 端口为 443)。请查看提供商的文档。
- 使用 multiGet 端点:不要循环调用
getCoin或getTransactionBlock,而是使用multiGetCoins或multiGetTransactionBlocks批量请求。这将往返次数从 N 次减少到 1 次。
- 分页策略:对于小型数据集,使用垂直分页(limit/offset);对于大型数据集,使用水平分页(基于游标),以避免深度偏移导致服务器端扫描。Sui 的
queryTransactionBlocks支持基于游标的分页。
- 缓存响应:对于不经常变化的数据(例如链标识符、总交易块数),在客户端或代理层缓存。如果可用,使用 HTTP 缓存头。
- 使用订阅:对于实时更新,使用 WebSocket 订阅而不是轮询。这减少了事件驱动应用的延迟。
- 选择专用端点:共享端点容易受到噪声邻居的影响。专用端点(例如通过 OnFinality 的 API 服务)提供一致的性能。
常见故障与修复
在测量或优化 Sui RPC 延迟时,您可能会遇到以下问题:
- 超时:如果请求超时,请增加客户端中的超时时间。Sui 的 JSON-RPC 对于重查询可能较慢;考虑使用具有更好流式传输的 gRPC。
- 速率限制:许多提供商强制执行速率限制。如果您遇到 429 错误,请实现指数退避或使用具有更高限制的提供商(参见 Sui RPC 速率限制和计算单元)。
- 不支持 gRPC:并非所有端点都支持 gRPC。请查看提供商的文档,或使用像 OnFinality 的 API 服务 这样提供两者的服务。
- 不正确的分页:在大型数据集上使用
limit和offset可能导致响应缓慢。切换到使用nextCursor的基于游标的分页。
- 网络拥塞:如果您与端点不在同一区域,延迟会增加。使用具有全球边缘缓存的服务提供商,或在靠近用户的位置部署自己的节点。
权衡与限制
虽然 gRPC 更快,但它需要支持 protobuf 的客户端,这可能会增加复杂性。GraphQL 很灵活,但如果过度获取,可能比 gRPC 慢。JSON-RPC 被普遍支持,但很冗长。
缓存可能会引入过时数据;使用适当的 TTL。订阅保持持久连接,可能会增加资源使用。
独立基准测试(例如 comparenodes)显示提供商性能差异很大。始终测量您自己的工作负载。Sui 的官方文档提供了性能特征,但实际延迟取决于您的具体用例和网络条件。
后续步骤:进一步阅读和工具
既然您了解了 Sui RPC 延迟,请探索以下资源以加深理解:
- Sui 网络概述 - 了解 Sui 的架构和端点。
- Sui RPC 指南(RPC 助手) - 获取 Sui RPC 使用的具体建议。
- Sui RPC 速率限制和计算单元 - 了解速率限制如何影响延迟。
- OnFinality 学习中心 - 更多教程和指南。
- RPC 定价 - 比较专用与共享端点的成本。
- API 服务 - 探索 OnFinality 的托管 RPC 端点。