第二章:系统架构概览
好,咱们直接进入正题。这一章我会把整个暗池执行引擎的骨架给你拆开来看。说白了,就是让你知道这玩意儿长什么样,各个零件怎么拼在一起,数据怎么跑,订单怎么走,以及——最关键的——延迟到底卡在哪儿。
我个人习惯,做任何系统之前,先画一张大图。脑子里没图,代码写得再快也是白搭。你想想看,高频交易里,一微秒的差距可能就是几百万的盈亏。架构设计要是出了问题,后面再怎么优化都是亡羊补牢。
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 去验证。我曾经优化了一个自认为很牛的地方,结果发现瓶颈根本没变——白忙活了两周。
所以,先测量,再优化。这是铁律。
我的经验:准备一个延迟基准测试框架,每次改动后自动跑一遍。如果延迟变差,代码直接打回。没有这个流程,你根本不知道自己的优化是正向还是负向。