1. 课程导入与问题定义
在证券、期货、数字资产等交易系统中,行情流是驱动策略、风控、撮合与展示的基础数据。它看起来只是"一串不断到达的报价消息",但在工程上,它同时具备三个关键特征:高频、有序、可重放。高频意味着单位时间内消息量巨大,单路行情在活跃时段每秒可达数千到数万条;有序意味着同一标的的行情变化存在严格的先后关系,后到的消息往往依赖先到的状态;可重放意味着行情流在断线重连、故障恢复时会被重新推送,历史片段可能再次出现。正是这三个特征,让"重复"和"错序"成为行情处理中最常见、也最容易被低估的两类问题。
先看重复消息的典型场景。最常见的是断线重连后的补发:客户端与行情网关断开后重新订阅,服务端为保证不丢数据,会把断线期间或最近一段快照重新推送,客户端就可能收到与断线前完全相同的消息。其次是多路冗余订阅:为提升可靠性,系统同时订阅主备两路行情源,两路内容高度重叠,若不做合并就会重复处理。第三是重试机制:上游推送超时后重发,而首次消息其实已经到达。重复消息本身不一定报错,但它会让成交量被重复累计、让策略信号被重复触发、让持仓统计出现偏差——这类错误往往在盘中不明显,收盘对账时才暴露。
再看序列错乱。行情消息通常带有序列号(Sequence Number)或时间戳,用于标识先后。错乱可能来自网络多路径传输:同一标的的消息经不同链路到达,后发先至;也可能来自多线程并发处理:接收线程把消息投入队列,多个工作线程并行消费,处理顺序被打乱;还可能来自上游本身的分片推送:不同频道、不同标的的消息独立编号,跨频道合并时顺序失去意义。错序的后果比重复更隐蔽:如果一条"撤单"先于对应的"下单"被处理,系统会认为撤的是一笔不存在的订单;如果最新价更新早于成交量更新,快照就会出现内部不一致。
上图把问题链路和防线放在一起看:重复与错序都发生在"行情源—网络—接收队列—业务处理"这条链路上,而去重与序列校验应当前移到接入层,在消息进入业务逻辑之前完成拦截。如果等到策略层才发现数据异常,往往已经产生了错误的信号或统计。
去重与序列校验的业务价值
这两项工作的价值可以归结为三点。第一是数据正确性:保证每一条行情只被处理一次、且按正确顺序处理,这是成交量、持仓、盈亏等一切派生指标可信的前提。第二是系统稳定性:重复消息会放大下游压力,错序会触发异常分支,接入层拦截能显著降低故障扩散面。第三是可追溯性:通过序列号缺口记录和去重命中日志,运维人员能快速判断"是上游漏发、网络丢包,还是本地处理异常",把排障从猜测变成定位。
举个具体的教学示例(以下参数为合理假设,非实测数据):某标的行情消息带单调递增的序列号 seq,客户端维护一个"已处理最大序列号" maxSeq 和一个近期序列号集合。收到消息时先判断:若 seq <= maxSeq 且该序列号已在集合中,判定为重复,直接丢弃;若 seq == maxSeq + 1,正常处理并更新 maxSeq;若 seq > maxSeq + 1,说明中间存在缺口,可先缓存该消息并启动一个短定时器(例如 200 毫秒)等待缺失消息到达,超时后向上游请求补发或记录缺口告警。这个"判重—判连续—判缺口"的三分支,就是接入层最核心的判断逻辑。
本课程的学习路径
围绕这条主线,课程将按"认识问题—掌握方法—落地实现—验证复盘"推进:先厘清行情流的消息模型与序列语义,再分别讲透去重的几种实现(集合判重、滑动窗口、布隆过滤器及其取舍)和序列校验的几种策略(严格连续、允许空洞、按频道独立编号),随后进入工程实现,包括接入层结构、缓存与超时设计、补发请求协议,最后通过故障注入与对账验证,确认去重与校验真正生效。本章作为导入,目标是让你建立"问题—防线—价值"的整体框架,后续每一章都会在这个框架上填入具体技术细节。
需要强调的是,去重与序列校验不是"加个 if 判断"就结束的小事。它涉及对上游协议的理解、对本地并发模型的把握、对异常处置策略的权衡。判断标准也很明确:在人为制造重复与乱序的测试中,业务层收到的消息应当既不重复、也不缺失、顺序正确;在真实盘中,序列号缺口应有日志可查、有告警可响应。带着这两个标准进入后续学习,会比单纯记忆算法更有方向感。