Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
RPC 故障排查阅读约 13 分钟

Hyperliquid WebSocket Ping/Pong:检测失效连接

了解 Hyperliquid 应用层 JSON ping/pong 心跳机制的工作原理,为什么 OPEN 状态的套接字仍可能是僵尸连接,以及如何构建检测器,在错过成交前自动重连。

TL;DR

Hyperliquid 的 /ws 端点使用应用层 JSON 心跳:服务器发送 {"method":"ping"} 并期望收到 {"method":"pong"},无流量的空闲连接可能被服务器端关闭。这与 TCP keepalive 和 WebSocket 协议级 ping/pong 帧不同,其重要性在于套接字在操作系统层面可能保持 OPEN,却静默地错过订单更新。健壮的客户端会按间隔发送文档规定的 ping,跟踪 lastMessageAt 时间戳,在限定窗口内未收到 pong 或数据时判定连接已死,并强制重连并重放订阅。检测到死连接窗口后,通过 REST 状态对账错过的 orderUpdates 以弥补缺口。

心跳分层:TCP Keepalive、WebSocket 帧与 Hyperliquid JSON Ping/Pong

三种不同的保活机制经常被混为一谈。TCP keepalive 是操作系统层面的探测,发送空 ACK 包以确认对端主机仍可达;它运行在应用层之下,不能证明你的 WebSocket 会话或其订阅是健康的。WebSocket 协议级 ping/pong 帧由 RFC 6455 定义,由 WebSocket 实现处理,通常不会暴露给应用代码。Hyperliquid 增加了第三层:应用层 JSON 心跳,记录在 Hyperliquid 文档的 Timeouts and heartbeats 中,其中 /ws 端点发送 {"method":"ping"} 并期望收到 {"method":"pong"} 回复,无流量的空闲连接可能被服务器端关闭。协议级定义请参见 RFC 6455 第 5.5.2 节(Ping 和 Pong 帧)。

由于这些层相互独立,TCP keepalive 通过或协议级 pong 成功并不能保证你的应用正在接收订单更新。Hyperliquid 心跳是保持应用会话存活的契约,并为你提供检测对端死亡的信号。将其视为订阅包装器的权威存活检查,并将其他两层视为传输卫生而非应用健康。心跳格式本身记录在 Hyperliquid 的 Timeouts and heartbeats 参考中。

  • TCP keepalive:操作系统层面,证明主机可达,而非会话或订阅健康。
  • WebSocket 协议 ping/pong:RFC 6455 帧,由库处理,通常对应用代码不可见。
  • Hyperliquid JSON ping/pong:应用层,有文档记录,是你的客户端应跟踪的信号。

为什么 OPEN 状态的套接字可能是错过订单更新的僵尸连接

半开连接发生在当一方认为会话存活,而另一方已停止传递数据时。本地套接字可能仍报告 OPEN,因为没有收到 FIN 或 RST,但 orderUpdates、trades 或 fills 均未到达。这就是 websocket.org - WebSocket Heartbeat: Ping/Pong and zombie connections 中描述的经典僵尸连接。在交易中,僵尸连接比干净断开更糟,因为你的代码认为已订阅,而成交却静默地未被处理。

Hyperliquid 文档规定无流量的空闲连接可能被服务器端关闭,这意味着服务器可能在安静时段断开你,但反过来也很危险:网络路径可能静默失败,而服务器仍认为你已连接。区分健康安静市场与死套接字的唯一可靠方法是要求定期证明存活。该证明就是 JSON pong,可选地由任何入站数据消息强化。

  • 僵尸状态:本地套接字 OPEN,无入站消息,未引发错误。
  • 风险:在静默窗口期间错过 orderUpdates 和成交。
  • 缓解:基于 pong 或任何数据帧的限定存活窗口。

订阅包装器设计:Ping 间隔、lastMessageAt 与死亡判定

包装器应承担四项职责:按间隔发送文档规定的 ping,在每个入站帧上更新 lastMessageAt 时间戳,在限定窗口内未收到 pong 或数据时判定连接已死,并强制重连并重放订阅。保持 ping 间隔短于死亡窗口,以便在判定失败前至少有一个 ping 能得到应答。例如,每 15 秒 ping 一次,静默 45 秒后判定死亡,但请根据你的风险承受能力调整这些值,而非盲目复制。

每个入站消息,包括 pong 和订阅数据,都应刷新 lastMessageAt。这可以防止在数据持续到达且 pong 可能较少的活跃市场中产生误报。当死亡窗口过去后,显式关闭套接字、清除定时器,并触发重连路径。重连路径必须重放订阅;否则你会重连到一个没有频道的静默套接字。此包装器依赖的生命周期细节请参见 Hyperliquid WebSocket 订阅:连接生命周期与重连。

  • Ping 间隔必须短于死亡窗口。
  • lastMessageAt 在 pong 和任何数据帧上刷新。
  • 死亡判定关闭套接字并启动重连加重放。

可运行的 Node.js 心跳包装器,带限定存活窗口

以下 Node.js 示例使用 ws 包,实现了文档规定的 JSON ping、lastMessageAt 跟踪器、死亡窗口和订阅重放。它有意保持最小化,以便你将其适配到你的订单管理逻辑。将订阅负载替换为你的实际频道,并将重连接入你的订单对账流程。

注意,ping 负载是文档规定的应用层消息,而非 WebSocket 协议帧。发送它可以保持会话存活,并引发 pong 以证明对端有响应。

const WebSocket = require('ws');

const URL = 'wss://api.hyperliquid.xyz/ws';
const PING_INTERVAL_MS = 15000;
const DEAD_WINDOW_MS = 45000;

let ws;
let pingTimer;
let lastMessageAt = 0;
let subscriptions = [];

function connect() {
  ws = new WebSocket(URL);

  ws.on('open', () => {
    lastMessageAt = Date.now();
    for (const sub of subscriptions) ws.send(JSON.stringify(sub));
    pingTimer = setInterval(() => {
      if (Date.now() - lastMessageAt > DEAD_WINDOW_MS) {
        console.error('dead connection detected, reconnecting');
        return reconnect();
      }
      ws.send(JSON.stringify({ method: 'ping' }));
    }, PING_INTERVAL_MS);
  });

  ws.on('message', (raw) => {
    lastMessageAt = Date.now();
    const msg = JSON.parse(raw.toString());
    if (msg.method === 'pong') return;
    handleMessage(msg);
  });

  ws.on('close', reconnect);
  ws.on('error', (err) => console.error('ws error', err.message));
}

function reconnect() {
  clearInterval(pingTimer);
  if (ws) ws.terminate();
  setTimeout(connect, 1000);
}

function handleMessage(msg) {
  // route orderUpdates, trades, fills here
}

function subscribe(sub) {
  subscriptions.push(sub);
  if (ws && ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(sub));
}

connect();

半开连接检测器与强制重连及订阅重放

半开检测器是将静默转化为行动的逻辑。它应在独立于 ping 定时器的定时器上运行,这样即使事件循环停滞或发送阻塞也不会阻止检测。检测器将 Date.now() 与 lastMessageAt 比较,当差值超过死亡窗口时,标记连接已死、终止套接字并安排重连。终止而非优雅关闭可避免等待可能永远不会响应的对端。

订阅重放必须是幂等的。将订阅存储在列表中,并在每次 open 事件时重新发送。如果你的客户端使用订阅 ID,请一致地重新生成或复用它们,以便关联响应。重放后,你的订单对账步骤应获取当前未结订单和近期成交,以弥补死亡窗口期间产生的任何缺口。姊妹指南 RPC WebSocket 无数据丢失重连 详细介绍了缺口恢复模式。

  • 检测器在自己的定时器上运行,而非在 ping 回调内。
  • 终止套接字以避免等待无响应的对端。
  • 每次 open 时重放订阅,然后对账订单状态。

检测到死亡窗口后对账错过的 orderUpdates

当检测器触发时,你有一个已知缺口:从最后处理的消息到新连接订阅并开始流式传输的时刻。在该窗口期间,orderUpdates 可能已发出并被错过。对账模式是在重连后通过 REST 获取权威状态,与本地订单簿比较,并应用任何差异。这比假设 WebSocket 重放会传递历史更新更安全,因为文档规定的心跳和订阅模型不保证回填错过的事件。

对于成交,trades 和 fills 频道机制决定了你可以重建什么。请查看 Hyperliquid WebSocket trades 和 fills 频道机制 以了解可用于对账的字段。如果你的策略依赖精确的成交顺序,请将死亡窗口视为数据缺口,并从 REST 对账,而非信任本地状态。

  • 在最后处理的消息时间戳处标记缺口起点。
  • 重连后通过 REST 获取未结订单和近期成交。
  • 在恢复实时处理前将差异应用到本地状态。

测量你自己的心跳行为:结果表指南

不要依赖通用数字来设置 ping 间隔或死亡窗口。针对你自己的端点和网络路径进行测量。运行受控测试:连接、订阅低流量频道,记录 ping 与 pong 之间的时间,以及连续数据消息之间的时间。然后通过阻断流量模拟死路径,记录检测器触发所需的时间。

使用如下结果表记录你的观察。用你自己的测量值填写;所示值为占位符,并非基准。此方法可复现,让你无需猜测即可根据风险承受能力调整死亡窗口。

  • Ping 到 Pong 往返:至少测量 100 个样本。
  • 数据到达间隔时间:在安静和活跃市场期间测量。
  • 检测器触发时间:模拟阻断路径后测量。
  • 误报率:统计健康会话期间检测器触发次数。
| Metric | Sample 1 | Sample 2 | Notes |
| --- | --- | --- | --- |
| Ping-to-pong RTT (ms) | | | |
| Max data gap (s) | | | |
| Detector fire time (s) | | | |
| False positives | | | |

排查持续断开与误判死亡

如果你的客户端频繁断开,首先检查你是否发送了文档规定的 ping。从不发送 ping 的客户端可能在空闲期间被服务器端关闭。其次,验证 lastMessageAt 是否在每个入站帧上更新,而不仅在 pong 上。忽略数据帧的客户端会在活跃市场期间误判死亡。第三,确认 ping 间隔短于死亡窗口;否则你可能会在 pong 有机会到达之前就判定死亡。

如果看到误判死亡,请增大死亡窗口或减小 ping 间隔,并记录事件前后的原始消息。如果断开与特定错误响应相关,请查看 Hyperliquid API 错误处理与订单拒绝 了解可能表明订阅格式错误而非传输失败的拒绝语义。对于延迟相关症状,请参见 Hyperliquid RPC 延迟 以区分网络延迟与连接死亡。

  • 缺少 ping:服务器可能关闭空闲连接。
  • lastMessageAt 过时:在所有入站帧上更新。
  • 间隔长于死亡窗口:误报。
  • 订阅格式错误:在归咎传输前检查错误响应。

应用层心跳的局限与权衡

应用层心跳增加了开销和复杂性。每个 ping 都是消耗带宽和处理的消息,短间隔会增加该成本。长间隔减少开销但增加检测死连接的时间,这直接增加错过成交的风险。没有普遍正确的设置;权衡取决于你的策略对错过更新的敏感度以及你对误报的容忍度。

另一个局限是心跳证明对端有响应,而非你的订阅正在传递数据。对端可以应答 pong,而特定频道因市场条件或订阅问题而静默。如果你的策略需要持续数据,请将心跳与每频道陈旧检查结合。最后,文档规定空闲连接可能被服务器端关闭,意味着你不能依赖安静套接字无限期保持打开;心跳是强制性的,而非可选的。

  • 短间隔:检测更快,开销更大。
  • 长间隔:开销更小,错过成交风险更高。
  • Pong 证明响应性,而非每频道数据流。
  • 空闲套接字可能被服务器端关闭;心跳是必需的。

下一步:加固你的 Hyperliquid WebSocket 客户端

首先实现上述包装器,并测量你自己的 ping 到 pong 和数据到达间隔时间。然后添加每频道陈旧检查和每次重连后运行的对账步骤。如果你需要行为可预测的托管端点,请查看 Hyperliquid RPC 端点(RPC Assistant) 和 API 服务 了解连接选项。网络层面背景请参见 Hyperliquid。

最后,在你的运行手册中记录所选间隔和死亡窗口,并通过模拟阻断路径定期测试检测器。OnFinality Learn 中心 包含有关订阅、成交和重连恢复的相关指南。如果你正在评估提供商方案,RPC 定价 列出了选项。目标不是零断开,而是零错过成交:一个提前触发并正确对账的检测器比一个永不重连的套接字更有价值。

  • 先实现并测量,再调整。
  • 添加每频道陈旧检查和重连后对账。
  • 通过模拟阻断路径测试检测器。
  • 在运行手册中记录间隔和死亡窗口。

永远不用担心基础设施

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

开始