Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
网络与协议指南12 分钟阅读

Sui RPC WebSocket 订阅:事件流与最佳实践

了解如何使用 Sui 的 WebSocket 订阅接口实时流式接收事件、检查点和纪元。包含可运行的 TypeScript 示例、预期的负载结构以及重连和速率限制的最佳实践。

TL;DR

Sui 的 WebSocket 订阅接口允许客户端实时接收事件、检查点和纪元更新。本指南解释了订阅流程,提供了可运行的 TypeScript 示例,并涵盖了重连、速率限制以及使用 GraphQL 作为替代方案的最佳实践。

直接回答:什么是 Sui WebSocket 订阅?

Sui 的 WebSocket 订阅接口允许您无需轮询即可实时接收网络更新。您可以订阅三种主要类型的数据:事件(例如,代币转账、NFT 铸造)、检查点(已提交的交易批次)和纪元更改(网络重新配置)。订阅使用标准的 JSON-RPC 2.0 协议通过 WebSocket 进行,Sui TypeScript SDK 提供了便捷的封装。本指南将引导您了解其机制,提供可运行的代码,并分享生产环境的最佳实践。

如果您想要快速答案:连接到 WebSocket 端点(例如 wss://rpc.testnet.sui.io),发送带有 suix_subscribeEvent 等方法的订阅请求,然后监听通知。服务器推送包含 params.result 字段的消息,其中包含事件数据。您必须手动处理重连,因为订阅在网络断开后不会保留。有关处理这些复杂性的托管 RPC 服务,请参阅我们的 Sui 网络页面

理解 Sui 的订阅架构

Sui 的 WebSocket 订阅基于 JSON-RPC 2.0 协议。客户端发送带有方法名称和参数的 subscribe 请求。服务器响应一个订阅 ID。此后,服务器发送 notification 消息(没有 id 的 JSON-RPC 请求),其中包含订阅 ID 和事件数据。要取消订阅,您需要发送带有订阅 ID 的 unsubscribe 请求。

订阅方法分组在 suix_ 命名空间下。主要方法有:suix_subscribeEvent(用于事件)、suix_subscribeCheckpoint(用于检查点)和 suix_subscribeEpoch(用于纪元更改)。这些方法在某些 Sui 版本中处于实验阶段,意味着 API 可能会发生变化。请始终查看 Sui RPC 最佳实践文档 以获取最新状态。

WebSocket 端点通常与 HTTP RPC 相同的主机,但使用 wss:// 而不是 https://。例如,如果您的 RPC URL 是 https://rpc.testnet.sui.io,则 WebSocket URL 是 wss://rpc.testnet.sui.io。OnFinality 为 Sui 提供 WebSocket 支持;请参阅我们的 RPC 助手 了解端点详情。

  • 订阅流程:请求 -> 订阅 ID -> 通知 -> 取消订阅
  • 方法:suix_subscribeEventsuix_subscribeCheckpointsuix_subscribeEpoch
  • 实验状态:API 可能随时更改,恕不另行通知

先决条件和设置

要运行示例,您需要 Node.js(v16 或更高版本)和 npm。安装 Sui TypeScript SDK 和 ws 包以用于原始 WebSocket 示例。如果您更喜欢原始客户端,可以使用任何 WebSocket 库。下面的示例使用官方 SDK 以保持清晰。

您还需要一个 Sui RPC 端点。您可以使用公共端点,如 wss://rpc.testnet.sui.io,或使用 OnFinality 的专用端点。对于生产环境,请考虑使用专用端点以避免速率限制;请参阅我们的 定价 了解选项。

npm install @mysten/sui.js ws
# 或者如果您使用最新的 SDK(截至 2026 年):
npm install @mysten/sui

可运行示例:使用 Sui TypeScript SDK 订阅事件

以下示例连接到 Sui 测试网,订阅所有事件,并打印前 5 个事件。它使用 SDK 中的 JsonRpcProvider。请注意,SDK 的 subscribeEvent 方法返回一个 Promise,该 Promise 解析为取消订阅函数。

事件负载结构包括 id(事件 ID)、type(事件类型字符串)、sender(地址)、timestampMsparsedJson(事件特定数据)。具体字段取决于事件类型。例如,0x2::coin::CoinBalanceChange 事件具有 coinTypeamountowner 字段。

import { JsonRpcProvider, testnetConnection } from '@mysten/sui.js';

const provider = new JsonRpcProvider(testnetConnection);

async function subscribeToEvents() {
  const unsubscribe = await provider.subscribeEvent({
    filter: { All: [] }, // 订阅所有事件
    onMessage: (event) => {
      console.log('收到事件:', JSON.stringify(event, null, 2));
    },
  });

  // 10 秒后取消订阅
  setTimeout(async () => {
    await unsubscribe();
    console.log('已取消订阅');
    process.exit(0);
  }, 10000);
}

subscribeToEvents().catch(console.error);

可运行示例:用于检查点订阅的原始 WebSocket 客户端

如果您更喜欢原始 WebSocket 客户端,可以使用 ws 包。此示例订阅检查点通知并打印检查点序列号。请求格式遵循 JSON-RPC 2.0:{ "jsonrpc": "2.0", "id": 1, "method": "suix_subscribeCheckpoint", "params": [] }

服务器将响应 { "jsonrpc": "2.0", "id": 1, "result": "<subscription_id>" }。然后,每个通知将如下所示:{ "jsonrpc": "2.0", "method": "suix_subscribeCheckpoint", "params": { "subscription": "<subscription_id>", "result": { "sequenceNumber": "123", "timestampMs": "...", ... } } }

const WebSocket = require('ws');

const ws = new WebSocket('wss://rpc.testnet.sui.io');

ws.on('open', () => {
  console.log('已连接');
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'suix_subscribeCheckpoint',
    params: [],
  }));
});

ws.on('message', (data) => {
  const msg = JSON.parse(data.toString());
  if (msg.id === 1) {
    console.log('订阅 ID:', msg.result);
  } else if (msg.method === 'suix_subscribeCheckpoint') {
    console.log('检查点:', msg.params.result.sequenceNumber);
  }
});

ws.on('error', (err) => console.error('WebSocket 错误:', err));

// 15 秒后关闭
setTimeout(() => ws.close(), 15000);

预期的事件负载结构及如何验证

当您订阅事件时,通知的 params.result 是一个对象,其结构如下(基于 Sui 的 RPC 模式):{ "id": { "txDigest": "...", "eventSeq": "..." }, "type": "0x2::coin::CoinBalanceChange", "sender": "0x...", "timestampMs": "...", "parsedJson": { ... } }parsedJson 因事件类型而异。

要验证您的订阅是否正常工作,您可以触发一笔交易(例如,转移 SUI)并查看相应的事件。或者,您可以将事件序列与检查点序列进行比较。有关事件类型的完整列表,请参阅 Sui 文档

如果您使用 OnFinality 的 RPC 服务,您可以通过 API 服务仪表板 监控您的使用情况和连接健康状况。

常见故障和修复:重连和速率限制

WebSocket 连接本质上是不稳定的。网络断开、服务器重启或空闲超时都可能导致连接中断。当连接断开时,您的订阅将丢失。您必须重新连接并重新订阅。Sui SDK 不会自动重连,因此您需要实现带有指数退避的重连循环。

另一个常见问题是达到速率限制。公共端点通常限制每秒的订阅数或消息数。如果超过限制,服务器可能会关闭连接或返回错误。为避免这种情况,请使用专用端点或减少订阅数量。OnFinality 提供可扩展的 RPC 服务;请参阅我们的 Sui RPC 指南 了解详情。

有关处理 WebSocket 断开的详细指南,请参阅我们的文章 WebSocket RPC 断开连接修复

  • 使用指数退避实现重连,并重新订阅所有活动订阅。
  • 使用 ping/pong 帧监控连接健康状况。
  • 使用专用端点以避免速率限制。

权衡和限制:WebSocket 与 GraphQL 与轮询

WebSocket 订阅提供低延迟的推送更新,但它们需要持久连接和手动重连逻辑。轮询更简单,但会引入延迟和额外负载。Sui 还提供支持订阅(通过 WebSocket)和查询的 GraphQL API。GraphQL 对于复杂查询更灵活,但有学习曲线。

WebSocket 订阅接口在某些 Sui 版本中处于实验阶段,因此可能会发生变化。对于生产环境,如果您需要稳定性,请考虑使用 GraphQL 订阅。然而,对于简单的事件流,WebSocket 是高效的。

OnFinality 支持 Sui 的 WebSocket 和 GraphQL;请参阅我们的 网络页面 了解可用的端点。

后续步骤和进一步阅读

既然您已经了解了 Sui WebSocket 订阅,您可以构建实时应用程序,如交易监控器、NFT 追踪器或分析仪表板。从上面的示例开始,并根据您的用例进行调整。

有关更高级的主题,请探索 Sui RPC 最佳实践文档 和我们的 学习中心。如果您需要可靠的 RPC 服务,请考虑 OnFinality 的 API 服务定价 以获取专用端点。

如果您遇到问题,我们的 RPC 助手 提供了故障排除提示。别忘了查看我们的 WebSocket 断开连接修复 以获得稳健的连接处理。

订阅生命周期和消息帧

当您与 Sui 建立 WebSocket 订阅时,客户端和服务器会进行特定的消息交换,理解这一点对于实现稳健的客户端至关重要。发送订阅请求(例如,{"jsonrpc":"2.0","id":1,"method":"suix_subscribeEvent","params":[...]})后,服务器会响应一条确认消息,其中包含订阅 ID。该 ID 对于该订阅是唯一的,并在后续通知和取消订阅时使用。确认消息的形式为 {"jsonrpc":"2.0","id":1,"result":"subscriptionId"}。订阅后,服务器会推送事件通知作为单独的消息,每个消息的结构为 {"jsonrpc":"2.0","method":"suix_subscribeEvent","params":{"subscription":"subscriptionId","result":{...}}}result 字段包含实际的事件数据。需要注意的是,通知中的 method 字段回显的是订阅方法,而不是用于请求的 JSON-RPC 方法。这允许客户端在多个订阅活动时将通知路由到正确的处理程序。

订阅生命周期中的错误处理经常被忽视。如果订阅失败(例如,由于参数无效或权限问题),服务器会发送一个错误响应,其 id 与请求相同,遵循标准的 JSON-RPC 错误格式。然而,在订阅建立后,错误也可能异步发生。例如,如果服务器在处理事件时遇到内部错误,它可能会发送一个 method 字段设置为 suix_subscribeEvent 的通知,并带有 error 字段而不是 result。客户端必须准备好处理成功和错误通知。此外,服务器可能会发送一个 method 通知,方法名为 suix_unsubscribeEvent,以指示订阅已终止(例如,由于服务器端超时)。正确处理这些消息可确保您的客户端能够优雅地应对意外终止。

  • 始终将确认响应中的 id 与请求匹配,以关联订阅。
  • 使用确认中的订阅 ID 来管理状态并路由传入的通知。
  • 处理通知中的 resulterror 字段;错误可能在订阅激活后发生。
  • 请注意,服务器可能会发送终止通知;如有需要,请实现逻辑以重新订阅。
// 处理订阅确认和通知的示例
ws.on('message', (data) => {
  const msg = JSON.parse(data);
  if (msg.id !== undefined) {
    // 这是对请求的响应(例如,订阅确认)
    if (msg.result) {
      console.log('已订阅,ID:', msg.result);
      subscriptionId = msg.result;
    } else if (msg.error) {
      console.error('订阅错误:', msg.error);
    }
  } else if (msg.method) {
    // 这是通知
    if (msg.params && msg.params.subscription === subscriptionId) {
      if (msg.params.result) {
        console.log('收到事件:', msg.params.result);
      } else if (msg.params.error) {
        console.error('通知错误:', msg.params.error);
      }
    }
  }
});

扩展订阅和幂等事件处理

随着应用程序的增长,您可能需要处理来自多个订阅的大量事件。一种常见的模式是在单个 WebSocket 连接上多路复用许多订阅。然而,这引入了管理多个订阅 ID 并确保事件正确处理的复杂性。一个关键的最佳实践是将事件处理视为幂等的。由于网络问题可能导致重复投递,您的事件处理逻辑应该能够多次处理同一事件而不会产生不利影响。Sui 事件包含一个 digest 字段(一个 base58 编码的哈希),用于唯一标识事件。通过维护最近处理的摘要集合(例如,在具有 TTL 的缓存中),您可以对事件进行去重,避免重复处理。

另一个扩展考虑是使用多个 WebSocket 连接来分散负载。Sui RPC 提供商通常对每个连接的订阅数量或通知速率施加限制。通过将订阅分片到多个连接(例如,基于事件类型或检查点范围),您可以提高吞吐量。然而,这需要仔细协调,以确保事件不会丢失或乱序处理。对于检查点订阅,您可以使用 startCheckpoint 参数从特定检查点恢复,但您必须跟踪每个连接最后处理的检查点。此外,考虑使用消息队列或流处理框架(如 Apache Kafka 或 Redis Streams)来缓冲事件,并将摄取与处理解耦。这允许您水平扩展处理而不会丢失事件。

  • 使用事件 digest 对事件去重;将已处理的摘要存储在具有 TTL 的缓存中。
  • 将事件处理程序设计为幂等的——处理同一事件两次不应产生副作用。
  • 考虑将订阅分片到多个 WebSocket 连接,以绕过每连接限制。
  • 跟踪每个连接最后处理的检查点,以便在断开后恢复。
  • 集成消息队列以缓冲事件,并将摄取与处理解耦以实现可扩展性。
// 使用带 TTL 的 Set 进行去重的示例
const processedDigests = new Map(); // digest -> timestamp
const TTL_MS = 60000; // 1 分钟

function handleEvent(event) {
  const digest = event.digest;
  const now = Date.now();
  // 清理旧条目
  for (const [key, ts] of processedDigests) {
    if (now - ts > TTL_MS) processedDigests.delete(key);
  }
  if (processedDigests.has(digest)) {
    console.log('忽略重复事件:', digest);
    return;
  }
  processedDigests.set(digest, now);
  // 处理事件...
}

用于订阅的原始 WebSocket 客户端示例

虽然 Sui TypeScript SDK 提供了便捷的封装,但理解原始 WebSocket 协议对于调试和不支持 SDK 的语言至关重要。下面是一个使用 Node.js 的 ws 库订阅 Sui 事件并接收通知的完整示例。此示例演示了完整的生命周期:连接、订阅、处理通知和取消订阅。它还包括错误处理和简单的重连策略。

该示例订阅所有事件(使用空过滤器),并记录事件类型和摘要。在实践中,您应该过滤事件以减少噪音和带宽。代码还展示了如何发送取消订阅请求并优雅地关闭连接。请注意,WebSocket URL 是标准的 Sui RPC 端点;您可以将其替换为您的提供商的端点。对于生产环境,请考虑使用像 reconnecting-websocket 这样的库来自动处理重连,但此示例提供了手动实现以保持清晰。

  • 该示例使用 ws 库;使用 npm install ws 安装。
  • 订阅请求使用 suix_subscribeEvent 和空过滤器来接收所有事件。
  • 请求中的 id 字段用于匹配确认响应。
  • 通知通过 method 字段识别,并包含订阅 ID。
  • 通过发送带有订阅 ID 的 suix_unsubscribeEvent 取消订阅。
const WebSocket = require('ws');

const ws = new WebSocket('wss://fullnode.mainnet.sui.io:443');
let subscriptionId = null;
let requestId = 1;

ws.on('open', () => {
  console.log('已连接');
  // 订阅所有事件
  const subscribeMsg = {
    jsonrpc: '2.0',
    id: requestId++,
    method: 'suix_subscribeEvent',
    params: [
      {
        filter: {} // 空过滤器表示所有事件
      }
    ]
  };
  ws.send(JSON.stringify(subscribeMsg));
});

ws.on('message', (data) => {
  const msg = JSON.parse(data);
  if (msg.id !== undefined) {
    // 对我们请求的响应
    if (msg.result) {
      subscriptionId = msg.result;
      console.log('已订阅,ID:', subscriptionId);
    } else if (msg.error) {
      console.error('订阅错误:', msg.error);
    }
  } else if (msg.method === 'suix_subscribeEvent') {
    // 通知
    const { subscription, result } = msg.params;
    if (subscription === subscriptionId) {
      console.log('收到事件:', result.type, result.digest);
    }
  }
});

ws.on('error', (err) => {
  console.error('WebSocket 错误:', err);
});

ws.on('close', () => {
  console.log('连接已关闭');
  // 在此处添加重连逻辑
});

// 稍后取消订阅:
function unsubscribe() {
  if (subscriptionId) {
    const unsubMsg = {
      jsonrpc: '2.0',
      id: requestId++,
      method: 'suix_unsubscribeEvent',
      params: [subscriptionId]
    };
    ws.send(JSON.stringify(unsubMsg));
  }
}

已知限制和假设

Sui 的 WebSocket 订阅 API 仍在发展中,在构建应用程序时,您应该注意几个限制和假设。首先,订阅方法(例如,suix_subscribeEventsuix_subscribeCheckpoint)不是稳定 JSON-RPC API 的一部分,可能会随时更改,恕不另行通知。Sui RPC 最佳实践文档 明确指出订阅是实验性的,可能会发生变化。因此,您应该固定 SDK 版本并监控 Sui 的发布说明以获取更新。

其次,提供商可能会对 WebSocket 连接施加自己的限制,例如每个连接的最大订阅数、消息速率限制或连接持续时间。例如,提供商可能将您限制为每个连接 100 个订阅,或在 5 分钟后断开空闲连接。这些限制不是标准化的,可能有所不同。请始终检查您的提供商的文档,并实现尊重这些约束的重连逻辑。第三,事件投递不保证恰好一次;重复可能发生,如果连接断开,事件可能会丢失。您应该设计您的系统以容忍至少一次投递,并使用检查点订阅进行可靠的重新播放。最后,订阅 API 目前不支持直接按交易摘要或发送方地址过滤;如果需要,您必须在客户端过滤事件。如果您订阅所有事件,这可能导致高带宽使用,因此请明智地使用过滤器。

  • 订阅 API 是实验性的,可能会更改;固定 SDK 版本并监控更新。
  • 提供商对连接、订阅和速率的特定限制很常见;请查阅文档。
  • 事件投递是至少一次;重复可能发生,断开时可能会错过事件。
  • 使用检查点订阅进行可靠的重新播放,避免错过事件。
  • 对于细粒度的事件选择,可能需要进行客户端过滤。

永远不用担心基础设施

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

开始