一、事件驱动架构基础
什么是事件驱动架构
先问大家一个问题:你平时写交易程序,是怎么处理行情数据的?
很多人第一反应是写个while循环,每隔几毫秒去查一下最新价格。嗯,这就是典型的轮询模式。但今天我要讲的,是另一种思路——事件驱动。
事件驱动架构,说白了就是:系统不主动去"问"数据来了没有,而是等数据来了"通知"你。就像你等外卖,不用每分钟去门口看一眼,外卖小哥到了会按门铃。
在交易系统里,这个"门铃"就是事件。一个行情更新、一笔成交回报、一条风控告警,都是事件。系统里各个模块都注册好自己关心的事件,事件来了,对应的处理函数自动触发。
核心思想:控制流反转。不是你的代码去调度数据,而是数据来调度你的代码。
我个人习惯把事件驱动架构拆成三个角色:
- 事件源——谁产生事件?比如交易所推送的行情、订单系统返回的成交
- 事件通道——事件怎么传递?内存队列、消息中间件、或者共享内存
- 事件处理器——谁来消费事件?策略模块、风控模块、记录模块
这三个角色解耦之后,系统的扩展性会好很多。我在项目中遇到过,早期用轮询方式写的一个回测框架,想加一个新策略,得改主循环代码。后来改成事件驱动,新策略只需要注册自己关心的事件就行,主流程完全不用动。
事件驱动 vs 轮询
咱们来做个对比。我画了一张图,把两种模式的差异讲清楚。
从图上能看出来,轮询模式最大的问题是——你永远在"猜"数据什么时候来。猜快了,CPU空转浪费资源;猜慢了,延迟就上去了。
我曾经在一个高频回测项目里做过对比测试。同样的策略逻辑,轮询版本跑完需要47秒,事件驱动版本只用了12秒。为什么差这么多?因为轮询版本大部分时间都在做"检查有没有新数据"这个无用功。
来,看个代码对比,更直观:
# 轮询模式
def polling_loop():
while True:
price = get_latest_price()
if price != last_price:
on_price_update(price)
last_price = price
time.sleep(0.01) # 10ms轮询间隔
# 事件驱动模式
def event_loop():
while True:
event = event_queue.get() # 阻塞等待
if event.type == 'PRICE_UPDATE':
on_price_update(event.data)
我的建议:如果你的系统延迟要求超过100ms,轮询勉强能用。但一旦要求低于10ms,必须上事件驱动。这不是选择题,是生存题。
事件驱动在交易系统中的应用场景
讲完概念,咱们聊聊实战。事件驱动在交易系统里到底怎么用?我归纳了三个最常见的场景。
场景一:行情处理
这是最基础的应用。交易所通过TCP/UDP推送行情,你的系统收到后包装成事件,分发给各个策略模块。
你想想看,如果同时跑10个策略,每个策略都去轮询行情,那得重复查10次。事件驱动模式下,行情只处理一次,然后广播给所有订阅者。
class MarketDataHandler:
def __init__(self):
self.subscribers = []
def on_tick(self, tick):
# 收到行情tick,包装成事件
event = TickEvent(tick)
# 广播给所有订阅者
for sub in self.subscribers:
sub.on_event(event)
场景二:订单管理
发单之后,成交回报什么时候来?没人知道。可能是1毫秒,也可能是1秒。用轮询去查订单状态?太傻了。
事件驱动模式下,订单系统只管发单,然后等着。交易所返回成交了,触发一个FillEvent,风控模块更新持仓,策略模块调整仓位,一气呵成。
避坑提醒:我曾经在订单状态处理上吃过亏。当时用了一个全局变量记录订单状态,结果并发场景下状态被覆盖了。后来改成事件驱动,每个订单独立的事件流,再也没出过问题。
场景三:风控检查
风控模块需要监控各种指标:持仓上限、撤单频率、资金使用率。这些检查如果放在主循环里做,会拖慢整个系统。
事件驱动模式下,风控模块独立运行,只处理它关心的事件。比如每来一个OrderRequestEvent,风控模块检查一下是否超限,没问题才放行。
| 事件类型 | 触发时机 | 处理模块 |
|---|---|---|
| TickEvent | 行情更新 | 策略模块、记录模块 |
| OrderEvent | 发单请求 | 风控模块、路由模块 |
| FillEvent | 成交回报 | 持仓模块、策略模块 |
| ErrorEvent | 系统异常 | 告警模块、日志模块 |
嗯,这里要注意一点:事件驱动不是银弹。如果你的系统逻辑极其简单,比如就一个策略跑一个品种,轮询反而更直接。但一旦涉及多策略、多品种、多模块协作,事件驱动的优势就体现出来了。
一句话总结:事件驱动让系统从"主动问"变成"被动等",从"猜数据"变成"等通知"。延迟更低,资源利用率更高,模块之间更解耦。
我个人做了这么多年交易系统,见过太多团队在架构选型上走弯路。有的上来就上事件驱动,结果业务逻辑还没搞清楚,先被异步回调搞晕了。有的死守轮询,系统延迟高得离谱还不自知。
我的建议是:先理解业务场景,再选架构模式。事件驱动是个好工具,但得用在合适的地方。