第二章:系统架构概览

好,咱们直接进入正题。这一章我会把整个暗池执行引擎的骨架给你拆开来看。说白了,就是让你知道这玩意儿长什么样,各个零件怎么拼在一起,数据怎么跑,订单怎么走,以及——最关键的——延迟到底卡在哪儿。

我个人习惯,做任何系统之前,先画一张大图。脑子里没图,代码写得再快也是白搭。你想想看,高频交易里,一微秒的差距可能就是几百万的盈亏。架构设计要是出了问题,后面再怎么优化都是亡羊补牢。

2.1 整体架构设计

这个引擎,我把它分成四层。不是那种花里胡哨的分层,是实战中磨出来的结构。

层级 名称 核心职责
L1 接入层 协议解析、会话管理、认证鉴权
L2 匹配引擎层 订单簿维护、撮合逻辑、价格发现
L3 风控与清算层 实时风控检查、净额结算、持仓管理
L4 存储与审计层 订单日志、快照持久化、回放重建

嗯,这里要注意。L1和L2之间,我建议走共享内存。为什么?因为网络栈的开销太大了。我在项目中遇到过,用TCP走本地回环,延迟直接多了3微秒。3微秒啊兄弟们,在暗池里这已经够对手抢跑两轮了。

核心原则:每一层之间的数据传递,必须做到零拷贝。能走指针就别走值,能走共享内存就别走socket。

2.2 核心模块划分

模块划分这事儿,我踩过不少坑。最早我按功能切,结果发现耦合太深。后来我换了个思路——按延迟敏感度切。

  • 热路径模块:订单接收、校验、撮合、成交回报。这些必须跑在同一个线程里,不能有锁。
  • 温路径模块:风控检查、资金校验。可以异步处理,但延迟不能超过100微秒。
  • 冷路径模块:日志写入、报表生成、监控告警。这些随便慢,别阻塞热路径就行。

我曾经犯过一个错误,把日志写入放在了热路径上。结果磁盘I/O一抖动,撮合延迟直接飙到毫秒级。那天的复盘会开得我头皮发麻。

避坑指南:热路径代码里,不要有任何内存分配操作。所有对象启动时预分配好,用对象池管理。我曾经因为一个new操作,让延迟从500纳秒跳到了2微秒。

2.3 数据流与订单流

咱们来看看订单从进门到出门,到底经历了什么。我画了一张图,你感受一下。

暗池执行引擎 - 订单流与数据流 交易客户端 接入层 协议解析/鉴权 匹配引擎 订单簿/撮合 风控与清算 实时检查/净额 存储与审计 日志/快照 订单(纳秒级) 共享内存 异步(微秒级) 批量写入 成交回报(纳秒级)

你看这个流程。订单从客户端进来,先过接入层做协议解析和鉴权。然后通过共享内存扔给匹配引擎。匹配引擎是核心中的核心,它维护着订单簿,执行撮合逻辑。成交之后,结果分两路走:一路直接返回给客户端,另一路异步发给风控和清算层做后续处理。

这里有个关键点:成交回报必须走热路径,直接返回。风控检查可以异步做,但不能拖慢主流程。我见过有些系统把风控放在同步路径上,结果延迟直接翻倍。

注意:异步处理不等于可以随便延迟。风控检查如果发现异常,必须能在100微秒内回滚交易。否则资金损失就大了。

2.4 延迟关键路径分析

好,到了最刺激的部分。延迟到底花在哪儿了?我拿一个真实的订单生命周期给你拆解一下。

// 伪代码:订单处理时间线
1. 网络接收: 500ns (DPDK优化后)
2. 协议解析: 200ns (固定格式报文)
3. 鉴权校验: 100ns (本地缓存)
4. 订单簿操作: 300ns (红黑树查找+插入)
5. 撮合逻辑: 400ns (价格时间优先)
6. 成交回报编码: 150ns
7. 网络发送: 500ns
--------------------------------
总计: 约2.15微秒

嗯,这是理想情况。实际上,你还要考虑缓存 misses、上下文切换、中断处理。我做过一次 profiling,发现最坑的是 TLB miss,一次就能吃掉 1 微秒。

为什么会这样?因为现代 CPU 的 L1 缓存命中只需要 4 个时钟周期,但一次 TLB miss 需要访问页表,可能要走内存,那就是 100 纳秒起步。你想想看,如果订单簿的数据结构跨了多个内存页,每次访问都 miss,那延迟就崩了。

我个人习惯,所有热路径数据都放在大页内存里。Linux 的 2MB 大页,配合 hugetlbfs,能显著减少 TLB miss。我在项目中试过,光这一项优化,延迟就降了 30%。

延迟关键路径总结:

  • 网络 I/O:占 40% 延迟,用 DPDK/内核旁路解决
  • 内存访问:占 30% 延迟,用大页 + 缓存行对齐解决
  • 锁竞争:占 20% 延迟,用无锁数据结构解决
  • 系统调用:占 10% 延迟,用用户态驱动解决

最后说一句。延迟优化不是一锤子买卖。你每改一个地方,都要用 perf 或者 eBPF 去验证。我曾经优化了一个自认为很牛的地方,结果发现瓶颈根本没变——白忙活了两周。

所以,先测量,再优化。这是铁律。

我的经验:准备一个延迟基准测试框架,每次改动后自动跑一遍。如果延迟变差,代码直接打回。没有这个流程,你根本不知道自己的优化是正向还是负向。


专注资料整理