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

Solana RPC WebSocket:Pubsub 方法、订阅与连接生命周期

了解如何使用 Solana 的 PubSub WebSocket API 进行实时账户、程序和日志订阅,并附有 Node.js 示例和故障排除指南。

TL;DR

本指南解释 Solana 的 PubSub WebSocket API,涵盖订阅方法、连接生命周期和常见故障。包括使用 @solana/web3.js 的可运行 Node.js 示例和故障排除清单。

直接回答:什么是 Solana RPC WebSocket?

Solana 的 RPC WebSocket API(也称为 PubSub)允许您通过持久 WebSocket 连接接收有关账户更改、程序活动和交易日志的实时通知。与轮询标准 JSON-RPC HTTP 端点不同,WebSocket 订阅会在数据可用时立即将数据推送到您的客户端,从而减少延迟和负载。官方端点是 ws://<ADDRESS>/wss://<ADDRESS>/(例如,本地验证器的 ws://localhost:8899,或公共 RPC 提供商的 wss URL)。本指南涵盖三种主要订阅方法——accountSubscribeprogramSubscribelogsSubscribe——以及槽位和根订阅,并通过可运行的 Node.js 示例解释完整的连接生命周期。

Solana 的 WebSocket API 是基于 WebSocket 的 JSON-RPC 2.0,采用请求/响应模式进行订阅,并使用通知格式传入事件。它与 EVM 风格的 eth_subscribe 不同,因为 Solana 的模型以账户为中心,并使用基于领导者调度的通知时序。理解这些差异对于构建可靠的实时应用程序至关重要。

  • 端点系列:ws://localhost:8899 用于本地开发,wss:// 用于公共 RPC 提供商(例如 wss://api.mainnet-beta.solana.com)。
  • 三种主要订阅方法:accountSubscribeprogramSubscribelogsSubscribe
  • 其他方法:slotSubscriberootSubscribesignatureSubscribe(尽管签名通知通常通过轮询处理)。
  • 通过 accountUnsubscribeprogramUnsubscribelogsUnsubscribe 等取消订阅。

Solana PubSub 如何工作:架构和通知流程

Solana 的 PubSub API 基于 JSON-RPC 2.0。要订阅,您发送一个包含方法名称和参数的请求,服务器响应一个订阅 ID。此后,每当订阅的事件发生时,服务器会向客户端发送通知消息(JSON-RPC 2.0 通知)。每个通知都包含订阅 ID 和数据负载。

通知节奏与 Solana 的领导者调度相关。对于 accountSubscribeprogramSubscribe,当账户或程序数据发生变化时发送通知,但仅在槽位被确认后。对于 logsSubscribe,每当触及订阅程序或账户的交易发出日志消息时发送通知。这意味着您可能会在领导者变更时看到通知突发,并且与实际交易处理相比可能会有轻微延迟。

官方 Solana 文档(Solana RPC WebSocket 方法)指定了确切的请求和通知格式。例如,订阅请求如下:{"jsonrpc":"2.0","id":1,"method":"accountSubscribe","params":["PUBKEY",{"encoding":"base64","commitment":"confirmed"}]}。响应是 {"jsonrpc":"2.0","result":"SUBSCRIPTION_ID","id":1}。然后通知到达,格式为 {"jsonrpc":"2.0","method":"accountNotification","params":{"result":{"context":{"slot":123},"value":{...}},"subscription":"SUBSCRIPTION_ID"}}

  • 请求格式:JSON-RPC 2.0 请求,包含方法、参数和 id。
  • 响应格式:JSON-RPC 2.0 响应,包含结果(订阅 ID)和 id。
  • 通知格式:JSON-RPC 2.0 通知,包含方法(例如 accountNotification)和包含订阅 ID 和结果的参数。
  • 承诺级别:processedconfirmedfinalized 影响通知发送的时间。

订阅方法深入探讨

三种主要订阅方法是 accountSubscribeprogramSubscribelogsSubscribe。每种方法都有特定的参数和通知负载。

accountSubscribe 监视单个账户的状态变化。参数:账户公钥(base58 字符串)和可选配置对象,包含 commitmentencoding。通知负载包括账户的数据、lamports、所有者、可执行标志、租金纪元以及槽位上下文。

programSubscribe 监视程序拥有的所有账户。参数:程序公钥和可选配置,包含 encodingfilters(例如 dataSizememcmp)。通知类似于账户通知,但针对程序拥有的任何账户。

logsSubscribe 监视交易日志。参数:过滤器("all"{"mentions": [pubkey]} 用于账户或程序提及,或 {"mentions": [pubkey], "commitment": "confirmed"})和可选承诺。通知包括日志数组、签名和槽位。

此外,slotSubscriberootSubscribe 提供槽位和根更新,用于跟踪链进度。signatureSubscribe 也可用,但通常较少使用,因为它需要预先知道签名。

  • accountSubscribe: params: [pubkey, {encoding, commitment}]
  • programSubscribe: params: [programId, {encoding, filters, commitment}]
  • logsSubscribe: params: [filter, {commitment}],其中 filter 是 "all"{"mentions": [pubkey]}
  • slotSubscribe: params: []
  • rootSubscribe: params: []
  • 取消订阅方法:accountUnsubscribeprogramUnsubscribelogsUnsubscribe 等。

使用 @solana/web3.js 的可运行 Node.js 示例

使用 Solana WebSocket 订阅的最简单方法是通过 @solana/web3.js 库,它封装了原始 WebSocket API。Connection 类提供了 onAccountChangeonProgramAccountChangeonLogs 等方法,自动处理订阅和通知解析。

下面是一个完整示例,订阅给定地址的账户更改、程序 ID 的程序账户更改以及程序的日志。它还演示了如何处理重连和清理。要运行它,请安装 @solana/web3.jsws(用于 Node.js 中的 WebSocket)。

预期输出:脚本将打印订阅 ID,然后记录到达的通知。由于实时数据取决于网络活动,您可能需要触发一些交易才能看到通知。该示例包含一个 30 秒后退出的超时。

  • 使用带有 WebSocket URL 的 Connection(例如 wss://api.mainnet-beta.solana.com)。
  • onAccountChange 返回订阅 ID(数字)。
  • onProgramAccountChange 接受程序 ID 和可选过滤器。
  • onLogs 接受过滤器(例如 'all'{mentions: [programId]})。
  • 始终处理 WebSocket 上的 errorclose 事件以实现重连。
// 安装:npm install @solana/web3.js ws
const { Connection, PublicKey } = require('@solana/web3.js');

// 替换为您的端点(例如 wss://api.mainnet-beta.solana.com)
const wsUrl = 'wss://api.mainnet-beta.solana.com';
const connection = new Connection(wsUrl, 'confirmed');

// 示例:订阅已知代币账户的账户更改
const accountPubkey = new PublicKey('YOUR_ACCOUNT_PUBKEY');
const subId = connection.onAccountChange(accountPubkey, (accountInfo, context) => {
  console.log('槽位', context.slot, '的账户更改');
  console.log('Lamports:', accountInfo.lamports);
  console.log('数据长度:', accountInfo.data.length);
}, 'confirmed');

// 订阅程序账户更改(例如 SPL Token 程序)
const programId = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
const programSubId = connection.onProgramAccountChange(programId, (accountInfo, context) => {
  console.log('槽位', context.slot, '的程序账户更改');
}, 'confirmed');

// 订阅同一程序的日志
const logsSubId = connection.onLogs(programId, (logs, context) => {
  console.log('槽位', context.slot, '的日志');
  console.log('签名:', logs.signature);
  console.log('日志:', logs.logs);
}, 'confirmed');

console.log('已订阅,ID:', subId, programSubId, logsSubId);

// 保持进程存活并处理清理
setTimeout(() => {
  connection.removeAccountChangeListener(subId);
  connection.removeProgramAccountChangeListener(programSubId);
  connection.removeOnLogsListener(logsSubId);
  console.log('已取消订阅并退出。');
  process.exit(0);
}, 30000);

// 预期输出(示例):
// 已订阅,ID: 1 2 3
// 槽位 123456 的账户更改
// Lamports: 2039280
// 数据长度: 165
// ...(通知按发生顺序出现)

连接生命周期:保持活动、重连和重新订阅

与 Solana PubSub 端点的 WebSocket 连接不是永久的。它可能因网络问题、服务器重启或空闲超时而断开。要构建健壮的客户端,您必须处理连接生命周期:连接、保持活动、检测关闭和重新订阅。

保持活动:大多数 WebSocket 服务器会定期发送 ping 帧。在 Node.js 中,ws 库自动响应 ping,但您也可以发送应用级 ping。Solana 官方文档未指定保持活动间隔,但通常每 30 秒发送一次 ping 以防止空闲超时。如果您使用 @solana/web3.js,该库在内部处理 ping,但您仍应监听 close 事件。

重连:当连接关闭时,您需要重新连接并重新建立所有订阅。@solana/web3.jsConnection 类不会自动重新订阅;您必须自己实现。常见模式是将订阅设置包装在函数中,并在 reconnect 时调用它。您可以使用 reconnecting-websocket 之类的库或编写自己的逻辑。

重新订阅:重新连接后,您必须再次调用订阅方法。跟踪您的订阅 ID 和使用的参数,以便使用相同的过滤器重新订阅。请注意,重新连接后订阅 ID 可能会更改。

有关通用 WebSocket 断开连接修复的深入探讨,请参阅我们的通用 WebSocket RPC 断开连接修复

  • 监听 openmessageerrorclose 事件。
  • 为重连尝试实现指数退避(例如 1 秒、2 秒、4 秒,最大 30 秒)。
  • 重新连接时,重新订阅所有活动订阅。
  • 使用心跳(ping/pong)检测死连接。
  • 考虑使用 reconnecting-websocket 之类的库进行自动重连。
// 使用 ws 和 @solana/web3.js 的重连逻辑示例
const WebSocket = require('ws');
const { Connection } = require('@solana/web3.js');

let connection;
let subscriptions = [];

function setupSubscriptions() {
  // 清除旧订阅
  subscriptions.forEach(id => connection.removeAllListeners(id));
  subscriptions = [];

  // 重新订阅
  const subId = connection.onAccountChange(accountPubkey, callback, 'confirmed');
  subscriptions.push(subId);
  // ... 添加其他订阅
}

function connect() {
  connection = new Connection(wsUrl, 'confirmed');
  connection._ws.on('close', () => {
    console.log('连接关闭。5 秒后重新连接...');
    setTimeout(connect, 5000);
  });
  connection._ws.on('open', () => {
    console.log('已连接。正在设置订阅。');
    setupSubscriptions();
  });
}

connect();

常见故障和故障排除清单

即使实现扎实,您也可能遇到问题。以下是常见故障及其修复方法。

负载下断开:高吞吐量订阅可能会压垮您的客户端或服务器。如果您看到频繁断开,请减少订阅数量或使用过滤器缩小数据范围。同时确保您的客户端快速处理消息;如果回调缓慢,可能会阻塞事件循环并导致超时。

通知延迟:通知与领导者调度和承诺级别相关。如果您需要更快的更新,请使用 processed 承诺,但请注意数据可能会被回滚。对于最终数据,请使用 confirmedfinalized

过滤器误用:对于 programSubscribedataSizememcmp 等过滤器必须正确格式化。memcmp 需要 offsetbytes(base58 编码)。如果您没有收到通知,请仔细检查过滤器逻辑。

连接错误:常见错误包括 Unexpected server response: 403(如果端点需要 API 密钥)或 WebSocket is closed before the connection is established。确保您的 URL 正确并且您有网络访问权限。

订阅 ID 未找到:如果您尝试使用无效 ID 取消订阅,将收到错误。跟踪 ID 并正确移除监听器。

  • 检查您的端点 URL:使用 wss:// 进行安全连接,并确保其可访问。
  • 验证承诺级别:processed 最快但可靠性较低;confirmed 是良好的默认值。
  • 使用过滤器减少数据量:程序订阅使用 dataSizememcmp
  • 监控您的 WebSocket 连接状态并实现带退避的重连。
  • 使用本地验证器(solana-test-validator)进行测试以避免速率限制。
  • 如果使用公共 RPC 提供商,请查看其文档了解速率限制和 WebSocket 特定规则。

权衡和限制

Solana 的 PubSub API 功能强大但也有权衡。通知不保证有序或完整;如果连接断开,您可能会错过事件。此外,通知节奏取决于领导者调度,因此您可能会看到活动突发而不是稳定流。

端点保留:公共 RPC 提供商可能对 WebSocket 连接有不同的保留策略。有些可能会在一定时间后断开空闲连接。始终实现重连逻辑。

通知时序因提供商和网络负载而异。对于关键应用程序,不要依赖精确时序;使用承诺级别来平衡速度和可靠性。

与 EVM eth_subscribe 相比,Solana 的模型以账户为中心,需要理解 Solana 的账户模型。例如,programSubscribe 类似于合约事件的 eth_subscribe,但作用于账户状态更改。

对于生产环境,考虑使用具有高可用性的专用 WebSocket 提供商。OnFinality 的 API 服务 提供可靠的 WebSocket 端点,您可以在我们的 RPC Assistant 中比较 Solana RPC 端点

  • 通知不保证无丢失;如果需要,请实现自己的对账。
  • 公共端点可能有速率限制;请查看您的提供商的文档。
  • WebSocket 连接是有状态的;生产环境必须重连。
  • 对于不可逆数据,使用 finalized 承诺,但预期更高的延迟。
  • 对于高吞吐量用例,考虑使用专用流服务,如 Helius LaserStream(独立第三方)。

后续步骤和进一步阅读

既然您了解了 Solana 的 WebSocket API,您可以构建实时应用程序,如交易监控器、投资组合跟踪器或套利机器人。从官方 Solana RPC WebSocket 方法 文档开始,了解确切的 JSON 格式。

如果您是 Solana 开发新手,请探索我们的 Solana 网络指南 以获取概述。有关定价考虑,请参阅 RPC 定价。如果您遇到断开连接问题,请参阅我们的 通用 WebSocket RPC 断开连接修复

要更广泛地了解 RPC 服务,请访问 OnFinality Learn 中心 获取更多教程和指南。

永远不用担心基础设施

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

开始