WebSocket 订阅使用指南

通过 BlockVectra WebSocket 端点使用 eth_subscribe 订阅 newHeads 与 logs。掌握连接鉴权、过滤条件限制、退避重连与数据补全。

BlockVectra 提供基于 TLS 的安全 WebSocket(wss://)连接,支持在单一长连接中处理实时以太坊事件订阅与标准 JSON-RPC 2.0 请求。

可用链

WebSocket 支持按链动态开放。客户端可通过公开的 GET /v1/chains 端点读取每个网络的 ws(布尔值)和 subscriptions(支持的订阅类型数组)确认实时状态。

下表列出当前已启用 WebSocket 的网络:

链{chain}WebSocket 端点(路径携带 Key)支持的订阅类型
Robinhood Chainrobinhood_mainnetwss://api.blockvectra.com/v1/robinhood_mainnet/{api_key}newHeads, logs

连接与鉴权

客户端通过标准 TLS 建立 WebSocket 连接(wss://)。API key 支持两种传入方式:

  • 路径携带 Key:wss://api.blockvectra.com/v1/{chain}/{api_key}
  • 请求头携带 Key:wss://api.blockvectra.com/v1/{chain},在 HTTP 升级(Upgrade)握手阶段传入 x-api-key: {api_key} 或 Authorization: Bearer {api_key} 请求头。

握手阶段准入检查

在建立 WebSocket 会话前,服务端会在 HTTP 升级握手阶段进行准入校验:

  • 身份认证:未提供 API key 返回 HTTP 401(missing_api_key);key 未知、已停用或已吊销返回 HTTP 401(invalid_api_key);若服务端 key 表数据同步暂不可用则返回 HTTP 503(auth_unavailable)。
  • 账户余额:账户预付余额为 0 或负数返回 HTTP 402(balance_exhausted);计费状态暂未确认返回 HTTP 503(billing_unavailable)。
  • 连接数限制:单 key 连接数超过每实例 20 条,或单账户连接数超过每实例 50 条返回 HTTP 429(ws_connection_limit)。
  • 链可用性:请求未知或未开放的链返回 HTTP 404(unknown_chain)。
  • 实例容量:当服务端实例连接数饱和或排队推送消息超过阈值时,握手返回 HTTP 503(overloaded)并附带 Retry-After 头。

连接建立后,客户端可以发送标准 JSON-RPC 2.0 请求(如 eth_blockNumber 或 eth_call)以及 UTF-8 文本帧格式的订阅控制方法。

订阅方法

服务端支持以太坊标准的发布/订阅接口:eth_subscribe 与 eth_unsubscribe。

newHeads

当新区块追加到链头时,实时推送区块头对象。

  • 订阅请求:
    {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
  • 订阅响应:返回十六进制订阅标识符:
    {"jsonrpc":"2.0","id":1,"result":"0x1"}
  • 推送通知帧:
    {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1","result":{"number":"0x1b4","hash":"0x..."}}}

logs

推送与指定过滤条件匹配的事件日志。

  • 过滤条件硬性要求:每个 logs 订阅过滤器必须指定 address(合约地址或地址数组)或 topic0(第一位置的主题,非 null)。两者均未指定的过滤请求(例如 {} 或 {"topics":[null,"0x..."]})会被服务端拒绝,返回错误码 -32602(logs_filter_required)。

  • 过滤规模限制:单次订阅最多指定 100 个地址;主题最多 4 个位置,每个位置最多 16 个候选哈希。

  • 实例过滤容量:若服务端实例上活跃的日志过滤器达到上限,订阅返回错误码 -32022(ws_filter_capacity)。

  • 链重组:发生区块重组时,被移除区块的日志推送通知中会标记 "removed": true。

  • 订阅请求:

    {"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}

eth_unsubscribe

通过订阅标识符取消指定订阅。

  • 退订请求:
    {"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x1"]}
  • 退订响应:
    {"jsonrpc":"2.0","id":3,"result":true}

运行示例

使用 viem v2 的 createPublicClient 与 webSocket 传输层连接。请将 {chain} 替换为目标链标识,{api_key} 替换为你的 API key:

import { createPublicClient, webSocket } from 'viem';

const apiKey = process.env.BLOCKVECTRA_API_KEY || '{api_key}';
const url = `wss://api.blockvectra.com/v1/{chain}/${apiKey}`;

const client = createPublicClient({
  transport: webSocket(url),
});

// 1. 订阅新区块头 (newHeads)
const unwatchBlocks = client.watchBlocks({
  onBlock: (block) => {
    console.log('收到新区块头:', block.number, block.hash);
  },
  onError: (error) => {
    console.error('watchBlocks 错误:', error);
  },
});

// 2. 订阅合约事件日志 (logs 过滤必须包含 address 或 topic0)
const unwatchEvents = client.watchEvent({
  address: '0x1234567890123456789012345678901234567890',
  onLogs: (logs) => {
    console.log('收到匹配日志:', logs);
  },
  onError: (error) => {
    console.error('watchEvent 错误:', error);
  },
});

关闭码与客户端处理

服务端主动终止 WebSocket 连接时会发送带有特定关闭码与简短原因的 Close 帧。下表列出服务端发出的关闭码及建议的处理方式:

关闭码原因字符串描述能否重试客户端动作
1001idle连接在 3600 秒(1 小时)内无活跃订阅且未收到任何消息是按需重新连接。
1003binary frames are not accepted客户端发送了二进制帧;服务端仅接收 UTF-8 文本帧否不要自动重连。修改客户端以发送纯文本帧。
1009message too large客户端入站消息超过 1 MiB(1,048,576 字节)限制否不要自动重连。拆分大请求或缩减请求体大小。
1012service restart服务端实例维护重启,或单连接已达最长生命周期(24 小时 ± 10% 抖动)是使用带随机抖动的退避策略重连,重新建立订阅并补全数据。
1013chain unavailable链在此监听端口停止接受 WebSocket 会话,或上游数据源断开是使用带全抖动的指数退避重连,重建订阅并通过 eth_getLogs 补全数据。
1013overloaded服务端实例所有连接的排队通知缓冲(256 MiB)达到上限是使用带全抖动的指数退避重连,重建订阅并通过 eth_getLogs 补全数据。
4402insufficient balance账户预付余额已耗尽否不要自动重连。充值后再重连。
4404invalid api keyAPI key 未知、已停用或已吊销否不要自动重连。在控制台检查并更换有效的 API key。
4408slow consumer会话推送队列超过 512 KiB(524,288 字节)时服务端以 4408 关闭并丢弃待发通知;4408 关闭帧排在已缓冲推送之后、最多等 2 秒,停止读取或读得远慢于推送速率的客户端往往收不到 4408,看到的是连接直接断开(浏览器报 1006)。是意外断开(没收到关闭帧,浏览器报 1006)按 4408 处理:退避重连,并减少订阅或加快读取;重新建立订阅并通过 eth_getLogs 补全数据。
4429push rate exceeded推送频率超过 1,000 次/秒(突发容限 10,000 次)是减少订阅数或收窄过滤条件;带退避重连后重建订阅并补全数据。
4503billing unavailable计费状态或 key 表数据短时间同步延迟(> 60 秒)是此为暂时状态,使用带全抖动的指数退避策略重连。

断线重连与指数退避

当服务端重启或网络断开时,为避免大量客户端同时重连导致流量洪峰,客户端必须遵循 API 规格 §15.4 要求的全抖动(full jitter)指数退避算法:

  • 退避公式:在第 n 次重试连接前(n = 0, 1, 2, ...),在区间内均匀随机选择等待时长:
    delay = random(0, min(20s, 0.5s * 2^n))
  • 参数规格(API 规格 §15.4):
    • 初始基础退避:0.5 s(0.5 s * 2^0 = 0.5 s)
    • 指数倍增乘数:2^n(0.5 s、1.0 s、2.0 s、4.0 s……)
    • 最大退避上限:20 s
    • 全抖动(Full jitter):在 0 到 min(20 s, 0.5 s * 2^n) 之间均匀选取伪随机值
    • 重置计数器:仅在会话持续稳定保持连接至少 60 秒 后,才将重试计数 n 重置为 0
    • 关闭码 1012:首次重试时引入随机初始延迟,避免集群重启时所有连接同步打散
    • 不可重连关闭码:遇到 4402、4404、1003 或 1009 时禁止自动重连。
    • 意外断开:意外断开(没收到关闭帧,浏览器报 1006)按 4408 处理:退避重连,并减少订阅或加快读取。

断线后的数据补全策略

WebSocket 订阅状态不跨连接持久化;断线期间产生的推送通知不会在服务端保留。客户端重连成功后,应按 API 规格 §15.5 实施补全:

  1. 通过 eth_getLogs 补全日志:
    • 持久化记录已处理并提交的最高区块高度(last_processed_block)。
    • 重连成功后立即调用 eth_subscribe("logs", ...) 恢复实时日志接收。
    • 调用 eth_getLogs 查询断线区间:设置 fromBlock 为 last_processed_block + 1,toBlock 为 "latest"(或新订阅收到的第一条通知区块),保持地址与主题过滤条件一致。
    • 若断线跨度超过 1,000 个区块(受限于 max_logs_block_range: 1000),将查询切分为每个最多 1,000 区块的连续批次。
    • 以 (blockHash, transactionHash, logIndex) 三元组对补全日志与实时通知进行边界去重。
  2. 通过 eth_getBlockByNumber 补全区块头:
    • 记录断线前收到的最新区块高度与哈希。
    • 重新订阅 newHeads。
    • 调用 eth_getBlockByNumber("latest", false) 确认当前高度;若存在缺失高度,按顺序拉取中间区块头并核对 parentHash 链连续性以排查可能发生的分叉重组。

限制与配额

限额数值超出时的结果
单个 API key 并发 WebSocket 连接数每个服务端实例 20 个协议升级握手返回 429(ws_connection_limit)
单个账户并发 WebSocket 连接数每个服务端实例 50 个协议升级握手返回 429(ws_connection_limit)
单个 WebSocket 连接订阅数100-32022 subscription_limit
单个 WebSocket 连接 newHeads 订阅数4-32022 subscription_limit
logs 订阅过滤条件要求必须指定 address 或 topic0(topics 的首个位置)-32602 logs_filter_required
入站 WebSocket 消息尺寸1 MiB(1048576 字节)连接关闭,关闭码 1009
单连接推送通知队列缓冲512 KiB(524288 字节)连接关闭,关闭码 4408(slow consumer)
每个服务端实例推送通知队列缓冲(所有连接合计)256 MiB(268435456 字节)下一条通知无法容纳的连接以关闭码 1013(overloaded)关闭;超过一半时,新 WebSocket 握手返回 503(overloaded,带 Retry-After),eth_subscribe 返回 -32026 ws_push_overloaded
单连接未读取应答缓冲16 MiB在客户端读取足够消息前,服务端不再从该连接读取后续消息
WebSocket 客户端未读取应答单次写入阻塞 30 秒直接断开连接(不发送关闭帧)
单连接推送通知速率1000 次推送/秒(突发 10000)连接关闭,关闭码 4429
空闲 WebSocket 连接3600 秒(1 小时)无订阅且无消息连接关闭,关闭码 1001
WebSocket 连接最长存活时间24 小时(带 ±10% 抖动)连接关闭,关闭码 1012

下一步

最后更新:

本页目录