技术博客

自选股与预警推送:价格触发的服务端实现

第 79 / 141 篇 · 2025-04-25 · secfin · 行情系统推送订阅管理

行情系统的分发问题之前是按"所有人看同样的市场"解的——组播天然做广播。自选股和预警把问题反过来:每个用户各看一小撮证券,服务端要在行情到达的瞬间算出"这路行情触发了谁的什么条件",还要扛住开盘瞬间全部预警集中触发的洪峰。这篇讲这个反向分发怎么建。

问题规模与朴素解法的死法

规模感:活跃用户千万级,人均自选 20~50 只(有上限,防止有人导全市场),预警规则人均几条。行情洪峰时每秒几十万条证券变动。朴素做法是每来一条行情遍历所有用户的所有预警——$O(\text{行情数} \times \text{规则数})$,乘出来是天数,直接出局。

倒排一下就行:不按用户找证券,按证券找用户。索引结构是 证券ID → 订阅了它的规则列表,每条规则挂阈值和比较方向。行情到达时,一条证券的变动只查一次索引,遍历的是"关心这只票的规则"——假设某证券平均被 5% 用户关注,匹配成本砍掉两个数量级。

$$ C_{\text{match}} = \sum_{\text{行情}} |\text{rules}(id)| \quad \text{而非} \quad N_{\text{rule}} \times \sum_{\text{行情}} 1 $$

倒排表是内存结构,和行情快照共享同一台服务器的地址空间(行情服务器直接在自己进程里跑预警匹配,读共享内存零拷贝)。规则变更(用户改阈值、加删预警)实时改倒排表——增删都是 O(1) 链表操作,和用户请求路径同步完成,不存在"加载生效延迟"。

触发语义:穿越,而不是满足

"股价高于 10 元就提醒我"——什么叫"高于"?如果开盘就在 10.5 元,要不要提醒?定义不清会出两种事故:不提醒,用户错过(报警失效感);反复提醒,用户被轰炸(通知垃圾化)。正确语义是穿越触发:

$$ \text{trigger} = (p_{\text{prev}} < \tau \land p_{\text{new}} \ge \tau) \;\lor\; (p_{\text{prev}} > \tau \land p_{\text{new}} \le \tau) $$

只在从一侧穿到另一侧的那一笔触发。实现上每条规则要记"当前在哪一侧"(一个比特),行情来时对比迁移方向。这个状态在行情乱序时的处理要小心:快照恢复或乱序重排会改写"上一笔"的参照,触发判定可能重复或漏掉——工程上的简化是接受微小失真(预警本来就是辅助功能),但必须在状态重置(用户改阈值、切数据源)时清空穿越状态,否则旧状态配新阈值会产生幽灵触发。

涨跌幅预警多一步:阈值是相对基准(昨收)的百分比,除权日昨收跳变,所有挂在该证券上的百分比规则基准全变。公司行动事件要主动广播给预警引擎,触发规则重锚定——这又是《K 线生成》里那张公司行动事件表的第二个消费者,事件驱动架构在这里省掉一堆对账麻烦。

触发之后的推送链路

匹配只是第一步,触发后的通知链路按延迟要求分级:

在线推送。APP 在线时长连接(WebSocket/厂商通道),服务端直接推,延迟亚秒级。这是主路径。

离线通知。APP 不在线走 APNs/厂商推送通道。注意行情触发洪峰会在开盘瞬间打爆推送通道的 QPS 限额(涨停潮那天,几万条预警同一秒触发)——必须做聚合:同一用户同一秒的多条触发合并成一条汇总通知("您的 3 只自选股触发预警"),聚合窗口 1~3 秒。用户感知不到 2 秒的合并延迟,推送通道的限额可是实打实的钱。

触达与重复抑制。规则触发后要记"已触发未复位"状态,避免价格围绕阈值震荡时每笔都通知。常见做法是触发后进入冷却期(如当日不再触发同方向),或要求反向穿越复位后才可再触发。冷却期的长短是产品决策,但系统要提供两种语义供配置。

放在链路哪里:不挡主路

最后一个架构问题:预警引擎在行情链路的什么位置。答案是挂在行情服务器的快照区旁边,而不是塞进分发主路径。理由:匹配需要读全量快照(涨跌幅预警要昨收、换手率预警要累计量),天然和快照同址;匹配失败或预警引擎过载,绝不能拖慢行情分发——主路(组播、代理)不依赖预警,预警进程崩了重启扫一遍快照和规则表就能恢复(穿越状态可以从规则表重建,代价是重启瞬间的少量重复触发,可接受)。

这条"旁路挂接、失败隔离"的原则和交易链路的事前风控放异步类似:辅助功能永远不能把主功能拖死。

预警匹配:倒排索引与穿越触发行情快照区共享内存倒排索引证券 → 规则链触发判定:穿越语义prev 与 new 分居阈值两侧600519 → u12 ≥1880 / u35 ≤5%600570 → u77 换手率 ≥8%000001 → u90 ≥15.2在线长连接直接推离线聚合1~3s 合并规则变更 O(1) 改倒排表;除权日事件广播,百分比规则重锚定昨收触发后冷却/复位两种语义可配;引擎旁路挂接,崩溃不影响分发主路涨停潮洪峰:同一用户多条触发合并成一条,保护推送通道 QPS 限额