一、事件驱动架构概述

做量化交易这些年,我越来越觉得,交易系统本质上就是个事件处理系统。你想想看,行情来了是事件,订单成交了是事件,风控触发了也是事件。说白了,整个交易流程就是事件在不断地产生、流转、被消费。

我个人习惯把事件驱动架构比作一个城市的交通系统。事件就像路上的车,事件总线就是主干道,各个处理模块就是不同的目的地。车要开到哪、走哪条路、什么时候该停,都得有个清晰的规则。

交易系统中的事件驱动模型

传统的轮询模式,说白了就是不停地问「有消息吗?有消息吗?」。这在低频交易时代还行,但到了高频场景,CPU全浪费在空转上了。

事件驱动模型就不一样了。它是被动触发的——有事件来了,系统才干活。没事件?那就歇着。这种模式在交易系统里特别合适,因为市场行情本身就是个典型的异步事件流。

我见过一个团队,早期用轮询方式处理行情,延迟动不动就几十毫秒。后来改成事件驱动,延迟直接降到微秒级。嗯,差距就是这么大。

事件驱动模型的核心组件其实就三个:

  • 事件源:谁产生事件?行情网关、订单系统、风控模块、用户操作……
  • 事件总线:事件怎么传?内存队列、消息中间件、共享内存……
  • 事件处理器:谁来消费?策略引擎、风控模块、日志系统、监控面板……

核心要点:事件驱动模型的关键在于「解耦」。事件源不需要知道谁在处理它,处理器也不需要关心事件从哪来。各模块只跟事件总线打交道,互不依赖。

事件的生命周期

一个事件从生到死,大致经历三个阶段。我在项目中遇到过不少因为生命周期管理不当导致的线上事故,所以这块我特别想讲细一点。

1. 产生(Event Generation)

事件产生的那一刻,其实就已经决定了它的「基因」。比如一个行情Tick事件,它天生就带着时间戳、品种代码、最新价这些属性。

这里有个坑——事件产生时一定要打上精确的时间戳。我曾经排查过一个诡异的问题:两个事件明明有先后顺序,但处理结果却是反的。最后发现,是事件产生时没记录时间,导致后续排序全乱了。

避坑指南:事件产生时,务必记录系统时间(最好是纳秒级)。别等到处理阶段再补时间戳,那时候已经晚了。

2. 路由(Event Routing)

事件产生后,得知道往哪送。这就是路由要做的事。

路由策略通常有几种:

  • 广播:所有处理器都能收到。适合行情数据这种「大家都要」的场景。
  • 点对点:只发给指定的处理器。适合订单这种「只有特定模块能处理」的场景。
  • 主题订阅:处理器按主题订阅,只收自己关心的。这是最灵活的方式。

我个人比较推荐主题订阅模式。你想想看,一个风控模块只关心风控事件,你非要把所有行情事件也塞给它,那不是浪费CPU吗?

3. 消费(Event Consumption)

事件到了处理器手里,怎么消费?同步还是异步?阻塞还是非阻塞?

这里有个原则:消费逻辑要尽量轻量。如果处理一个事件要花几毫秒,那整个系统的吞吐量就上不去。

我记得有一次,一个同事把数据库写入操作直接放在事件处理器里。结果行情一快,事件队列直接爆了。后来改成异步写入,问题就解决了。

小技巧:事件消费时,如果要做耗时操作(比如写数据库、发网络请求),一定要异步化。可以用线程池或者消息队列来解耦。

事件分类的核心维度

事件多了,就得分类。不然系统会乱成一锅粥。我一般从三个维度来给事件分类:

1. 业务类型

这是最直观的分类方式。交易系统里常见的事件类型有:

事件类型 举例 典型处理逻辑
行情事件 Tick、K线、深度 更新策略状态、触发信号
订单事件 下单、撤单、成交 更新订单簿、计算持仓
风控事件 超限、异常、熔断 暂停交易、报警通知
系统事件 启动、关闭、心跳 状态切换、健康检查

2. 紧急程度

不是所有事件都值得立刻处理。我习惯把紧急程度分成三级:

  • 高优先级:风控事件、资金变动。必须立即处理,延迟不能超过微秒级。
  • 中优先级:订单状态变更。可以容忍毫秒级延迟。
  • 低优先级:日志、统计信息。可以排队慢慢处理。

为什么要分优先级?说白了,就是保证关键事件不被淹没。你想想看,如果风控事件和行情事件排在一个队列里,行情一多,风控事件就被堵住了。那万一市场剧烈波动,系统可能来不及反应。

实践建议:用多个优先级队列来隔离不同紧急程度的事件。高优先级队列用独立的线程池,保证它永远有资源可用。

3. 影响范围

这个维度很多人会忽略,但其实特别重要。影响范围决定了事件处理失败后的后果:

  • 局部事件:只影响单个策略或单个品种。比如某个策略的止损触发,其他策略不受影响。
  • 全局事件:影响整个系统。比如账户资金不足、交易所断连。
  • 级联事件:一个事件会引发一连串后续事件。比如风控熔断后,所有策略都要停止交易。

我处理过一个级联事件的案例:某个策略的止损触发后,因为没处理好级联逻辑,导致其他策略也跟着误操作。后来我们给每个事件都加了个「影响范围」字段,处理前先判断要不要通知其他模块。

知识体系总览

说了这么多,我画了张图帮你梳理一下整体结构。这张图把事件驱动架构的核心要素串起来了:

事件驱动架构知识体系 事件源 行情网关 / 订单系统 事件总线 路由 / 分发 / 优先级 内存队列 / 消息中间件 事件处理器 策略 / 风控 / 日志 事件生命周期:产生 → 路由 → 消费 业务类型 行情 / 订单 / 风控 / 系统 紧急程度 高 / 中 / 低 影响范围 局部 / 全局 / 级联 核心目标:解耦、高效、可扩展 事件源不关心谁消费,处理器不关心事件从哪来

这张图把事件驱动架构的核心要素都串起来了。从左到右是事件流的方向,从上到下是分类维度。你设计系统时,可以拿这张图当个检查清单,看看每个环节是不是都考虑到了。

我的经验:刚开始做事件驱动架构时,别想着一步到位。先搭个简单的内存队列版本,跑通了再考虑引入消息中间件。我见过太多团队一上来就上Kafka,结果连基本的事件格式都没定好,最后改得痛不欲生。

好了,事件驱动架构的概述就聊到这。下一节我们会深入事件总线的具体实现,包括怎么设计优先级队列、怎么处理事件积压、怎么保证事件不丢失。这些都是在实战中踩过坑才总结出来的经验,到时候再细聊。


公众号:蓝海资料掘金营,微信deep3321