WebSocket 行情服务的可观测性:为什么连接在线仍可能没有有效数据?

摘要:WebSocket 行情服务的监控习惯,往往止步于“连接状态”和“在线率”。但心跳正常不等于数据有效,重连成功不等于连续性恢复,订阅列表完整不等于下游消费正常。本文从一次凌晨监控事故切入,提出一套面向行情 WebSocket 的可观测性指标体系,覆盖连接状态、数据健康、订阅一致性和下游消费状态,并讨论如何用 REST 快照做独立校准、什么场景值得建立完整体系。关键词:WebSocket 行情监控、可观测性、断线重连、数据健康。


一、一次凌晨事故:在线率 100%,数据停了 40 分钟

凌晨四点,监控大盘显示 WebSocket 行情服务的在线率 100%。心跳正常,连接时长稳定,没有任何告警触发。

但早盘开盘后,交易员发现价格线有明显的跳空缺口。排查日志发现,凌晨三点四十分到四点二十分之间,WebSocket 连接虽然保持在线,心跳 ping/pong 也正常往返,但服务端没有推送任何 ticker 消息。行情数据在这四十分钟里是空白的。

为什么在线率 100% 却丢了数据?因为监控只看“连接在不在”,没看“数据来没来”。心跳只证明 TCP 连接存活和协议层健康,不证明业务数据在持续流动。服务端可能因为内部限流、频道异常、数据源上游中断等原因,维持着连接但停止推送——而客户端如果只检查心跳,对此一无所知。

在线率不等于数据有效。重连成功不等于连续性恢复。订阅列表完整不等于下游消费正常。

这次事故之后,我们把监控从“连接维度”扩展到了四个维度:连接状态、数据健康、订阅一致性和下游消费状态。这不是监控系统的升级,是可观测性思路的转变——从“服务在不在”转向“数据对不对”。


二、传统监控的三个盲区

大多数行情 WebSocket 接入的监控,建立在三个隐含假设上。这三个假设在正常运行条件下成立,但在边界条件触发时恰好是故障的起点。

盲区一:心跳 = 数据正常

心跳机制的设计初衷是检测连接是否存活。但 ping/pong 只经过 WebSocket 协议层,不经过业务数据路径。服务端可以在维持心跳的同时停止推送行情消息——上游数据源中断、内部频道异常、符号订阅失效,都可能导致“连接在线但数据空白”。只检查心跳,等于用协议层的健康推断业务层的健康。

盲区二:重连成功 = 连续性恢复

WebSocket 断开后自动重连成功,很多监控系统就此标记“已恢复”。但重连只恢复了连接,不恢复数据连续性。断开期间的行情变动不会自动回补,重连前的订阅列表可能丢失,重连后收到的第一条消息可能与断线前最后一条之间存在时间缺口。如果没有“最近一次有效行情时间”和“空窗记录”这两项指标,这个缺口会在不知情的情况下永久留在数据里。

盲区三:订阅列表一致 = 下游消费正常

订阅指令发送成功,返回了确认消息,监控就此认为“推送正常”。但从服务端推送到客户端接收,再到下游消费——写入数据库、触发计算、更新缓存——中间还有多个环节。下游消费线程阻塞、数据库写入失败、回调处理过慢导致消息积压,这些都不会反映在 WebSocket 的订阅状态里,但会导致数据在“最后一公里”丢失。

这三个盲区指向同一个问题:连接层的监控指标无法穿透到业务数据层。要回答“数据还活着吗”,需要一套跨越连接、数据、订阅和消费四个维度的指标。


三、可观测性指标清单

以下七项指标覆盖从连接层到业务层的四个维度。指标 1-2 面向连接生命周期,指标 3-5 面向数据健康,指标 6 面向订阅一致性,指标 7 面向下游消费。

3.1 连接维度

指标 1:连接状态

不只是布尔值“在线/离线”。连接状态应至少区分:未连接、连接中、已连接未订阅、已连接已订阅、主动关闭中、异常断开。状态机的每个转换节点都应该有日志记录时间戳和触发原因。长期运行的服务,还应记录当前连接建立时刻和持续时长——如果持续时长突然归零但状态仍显示“已连接”,说明发生了重连,此时必须触发订阅恢复检查。

指标 2:重连次数与空窗记录

每次重连记录三项:断开时间、重连成功时间、空窗时长。空窗时长不等于断开时长——如果重连后还需要重新订阅并等待第一条消息,空窗应算到第一条有效消息到达为止。连续重连次数超过阈值应触发告警,而不是等到指数退避耗尽才通知。

3.2 数据健康维度

指标 3:最近一次有效消息时间

指 WebSocket 连接上最后一次收到“可解析的、包含有效字段”的消息的时间。心跳消息不计入。如果当前时间与最近一次有效消息时间的差值超过阈值,即使心跳正常,也应判定为“数据可能中断”。这是发现“连接在线但数据空白”最直接的信号。

指标 4:最近一次有效行情时间

与“有效消息时间”不同,这项指标取自消息内部的业务时间戳。如果消息内部的时间戳停留在较长时间之前,而当前时间在不断增长,说明服务端可能在重放旧数据或上游数据源已延迟。有效行情时间与当前时间的差值,反映的是“数据的业务新鲜度”,而不是“连接的协议新鲜度”。业务时间戳的语义需要按具体接口确认,不同接口和品种下的时间戳单位可能存在差异。

指标 5:无效消息或解析失败数量

统计两类异常:JSON 解析失败的消息数,以及字段缺失或类型错误的消息数。这项指标不判断单条消息的业务含义是否正确,只判断消息是否满足最小结构契约。如果解析失败率在正常运行时趋近于零而突然出现非零值,通常是上游数据格式变更或网络中间件截断的前兆。

3.3 订阅一致性维度

指标 6:当前订阅集合

服务端认为已订阅的品种集合,与客户端本地记录的期望订阅集合,应定期做差集比较。重连后尤其需要核对——旧连接的订阅随连接销毁,重连后的订阅指令是否被完整执行,不能只靠“发送成功”来确认,应对比“发送的品种集合”与“服务端返回确认的品种集合”。集合不一致时,缺失的品种不会有任何报错,只是不会收到推送。

3.4 下游消费维度

指标 7:下游消费状态

这项指标不在 WebSocket 连接本身,而在消息的消费端。如果消费者将行情数据写入数据库,应监控最近一次写入时间。如果消费者触发计算任务,应监控最近一次计算完成时间。如果消费者维护内存缓存,应监控缓存的最近更新时间。下游消费中断时,WebSocket 连接可能一切正常——推送在到达,但没人处理。这是可观测性链条的最后一环,也是离业务最近的一环。


四、用 REST 快照做独立校准:TickDB 的多入口分工示例

上面七项指标中,大部分适合在 WebSocket 客户端内部持续采集。但有两项需要外部参照才能得出可靠结论:指标 4(有效行情时间)和指标 6(订阅集合)。原因在于,WebSocket 是一路单向数据流——客户端只知道“收到了什么”,不知道“应该收到但没收到什么”。

这恰好是 REST 接口可以发挥独立校准作用的场景。以 TickDB 这类同时提供 REST 和 WebSocket 入口的行情 API 为例:

WebSocket 负责持续采集:指标 1(连接状态)、2(重连与空窗)、3(有效消息时间)、5(解析失败数)在 WebSocket 客户端内部维护。它们依赖连接生命周期和消息流,天然适合在长连接上持续采集。

REST 负责独立校准:指标 4(有效行情时间)如果只依赖 WebSocket 推送内部的业务时间戳,无法区分“市场真的没有成交”和“推送中断了”。用 REST ticker 快照定期查询同一品种,与 WebSocket 侧记录的最新行情时间对比,可以区分这两种情况——REST 返回的时间戳比 WebSocket 侧记录的新,说明推送有延迟或丢失;两者一致但都较旧,说明市场确实没有新成交。指标 6(订阅集合)也可以通过定期用 REST 查询品种列表或 ticker 快照,与 WebSocket 实际收到的品种集合做差集,发现那些“订阅了但没收到推送”的品种。

AI 工具入口负责消费验证:指标 7(下游消费状态)可以通过定期让 Agent 通过 MCP 工具查询行情,并对比查询结果与下游缓存或数据库中的值是否一致来间接验证。如果 Agent 拿到的价格与下游记录的价格出现系统性偏差,说明消费链路某环节出了问题。

这三个入口不是冗余——WebSocket 是持续路径,REST 是独立参照,AI 工具是消费验证。三者覆盖的指标维度不同,出问题时交叉比对可以快速定位故障层。


五、什么时候值得建立完整指标体系

七项指标全部落地需要额外的采集、存储和告警逻辑。不是每个行情接入场景都需要全套。

完整指标体系值得做的场景:行情数据直接驱动交易决策或风控告警,数据中断的代价是漏单或误判;WebSocket 连接需要长时间保持,中间不允许出现未发现的空窗;多个下游消费者依赖同一路推送,链路复杂,故障定位需要交叉比对。

一个“最小可运行”集:指标 1(连接状态)+ 指标 3(最近有效消息时间)。前者覆盖连接层,后者覆盖数据层。即使只有一个心跳检查和一条“距上次有效消息不超过 N 秒”的告警规则,也能发现绝大多数“连接在线但数据空白”的事故。其余指标可以在故障复盘时逐步补上。

不需要完整指标的场景:一次性脚本、定时拉取任务、或数据中断不产生业务后果的展示类场景。这些场景下,直接检查返回的 codetimestamp 新鲜度通常足够。

完整指标体系的成本不在实现难度,在于长期维护——每增加一项指标,就多一个需要持续关注和校准的维度。按场景选择深度,比一次性铺满所有指标更可持续。


六、结束语

WebSocket 行情服务的监控容易停留在“在线率”上,因为在线率是看得见、容易度量的。但真正让数据出问题的,往往是那些“在线率正常”掩盖下的空白——推送中断、时间戳停滞、订阅丢失、下游消费阻塞。这七项指标不是要取代心跳检查,而是补充心跳看不到的东西:数据在不在流动、数据是否新鲜、消费是否正常。

如果你的 WebSocket 行情服务目前只监控了在线率,可以先从最简单的改造开始:加一条“最近一次有效消息时间超过阈值告警”规则。这比心跳更能回答一个关键问题——数据还活着吗?


posted @ 2026-06-12 18:00  Srena量化员  阅读(2)  评论(0)    收藏  举报