系统架构总览:分层架构设计、模块划分、数据流与消息流

做市商报价引擎,说白了就是一个「高频决策+低延迟执行」的系统。我这些年拆过不少报价引擎,发现一个规律:架构设计的好坏,直接决定了你是在赚钱还是在给交易所交手续费。今天咱们就把它拆开来看,从分层、模块到数据流,一条线捋清楚。

一、分层架构设计:为什么非分不可?

我个人习惯把报价引擎分成四层。你想想看,如果所有逻辑都揉在一个进程里,改个风控参数要重启整个系统,那多可怕?分层的好处就是:每层各司其职,互不干扰

核心分层(从上到下):

  • 接入层(Gateway Layer):负责跟交易所、行情源、交易终端打交道。说白了就是「翻译官」,把各种协议(FIX、WebSocket、UDP Multicast)转成内部统一格式。
  • 策略层(Strategy Layer):这是报价引擎的大脑。做市策略、定价模型、订单生成都在这里。我见过不少团队把策略和风控混在一起,结果出了bug都不知道是策略算错了还是风控拦错了。
  • 执行层(Execution Layer):负责订单管理、路由、撤单重发。这一层最讲究「快」和「稳」。嗯,这里要注意:执行层绝对不能有阻塞操作,我曾经因为一个日志打印的锁,导致撤单延迟了2毫秒,直接被交易所罚了钱。
  • 风控层(Risk Control Layer):资金检查、仓位限制、报价频率控制。这一层是「刹车片」,平时用不上,但关键时刻能救命。

这四层之间怎么通信?我建议用内存队列 + 事件驱动。为什么不用RPC?因为RPC的序列化开销在微秒级别,对于做市商来说,每一微秒都是钱。

二、模块划分:每个模块该干什么活?

模块划分这事,我踩过不少坑。早期我设计过一个「万能模块」,什么都能干,结果代码耦合得像一团麻花。后来我学乖了,一个模块只做一件事,做好一件事

模块名称 核心职责 避坑指南(我的血泪史)
行情订阅模块 订阅多交易所行情,去重、对齐时间戳 我曾经没做时间戳对齐,结果策略同时用了两个交易所的延迟数据,报价直接飞了
订单簿管理模块 维护本地订单簿快照,计算中间价、价差 注意:订单簿重建逻辑一定要写单元测试,我见过有人因为增量更新漏了一条,整个订单簿就歪了
定价引擎模块 根据库存、波动率、对手方行为计算报价 定价模型别搞太复杂,先跑通再优化。我一开始就上了深度学习,结果延迟爆炸,后来换回线性模型,反而赚钱了
订单管理模块 订单生命周期管理:创建、确认、撤销、成交 一定要处理「幽灵订单」——就是交易所确认了但你这边没收到的情况
风控检查模块 实时检查每笔报价是否合规 风控逻辑要放在报价发出前,而不是发出后。我见过有人把风控放在成交回报里,那还有什么意义?
日志与监控模块 记录所有事件,监控延迟、丢单率 日志别用同步IO,用异步写入。否则行情高峰时,日志本身就会成为瓶颈

三、数据流:数据是怎么在系统里跑的?

数据流是架构的灵魂。我习惯把数据流分为上行流下行流。上行流是行情数据,下行流是订单数据。两者不能混在一起,否则会出现「行情还没到,订单先发出」的荒唐事。

一个典型的数据流路径:

  1. 交易所行情 → 接入层(Gateway) → 行情订阅模块 → 订单簿管理模块 → 定价引擎模块
  2. 定价引擎算出报价 → 风控检查模块 → 订单管理模块 → 接入层 → 交易所
  3. 交易所成交回报 → 接入层 → 订单管理模块 → 策略层(更新库存状态)

你看,整个流程是单向的、无环的。一旦出现循环依赖,比如策略层又去修改行情数据,那系统就乱套了。

这里有个关键点:数据流中的每个节点都要有超时机制。我曾经遇到过行情模块卡住,结果下游的定价引擎一直在用旧数据报价,那几分钟亏了十几万。后来我加了个「心跳检测」,超过100毫秒没收到新行情,直接暂停报价。

四、消息流:模块之间怎么说话?

模块之间的消息传递,我推荐用事件总线(Event Bus)。为什么不用消息队列?因为消息队列的延迟太高了(毫秒级),而事件总线可以做到微秒级。

消息流的设计原则就三条:

  • 异步非阻塞:发送消息后立即返回,不等待处理结果。如果需要结果,用回调或Future。
  • 消息不可变:消息一旦发出,就不能被修改。否则你想想看,A模块改了消息内容,B模块拿到的是改过的,C模块拿到的是没改的,那不乱套了?
  • 消息有唯一ID:每条消息都要有全局唯一的ID,方便追踪和排错。我习惯用「时间戳+机器ID+序列号」的格式。

特别注意:消息丢失问题

事件总线虽然快,但它不保证消息不丢失。对于关键消息(比如成交回报),我建议同时走两条路径:一条走事件总线(低延迟),一条走可靠队列(高延迟但保证不丢)。这样即使事件总线丢了消息,也能从队列里恢复。

五、架构总览图:一张图看懂全局

下面这张图是我自己画的,把上面说的分层、模块、数据流都串起来了。你看一眼就能明白整个系统是怎么运转的。

做市商报价引擎架构总览 接入层(Gateway Layer) FIX协议 | WebSocket | UDP Multicast | 统一消息格式转换 数据流方向:交易所 ↔ 内部系统 策略层(Strategy Layer) 定价引擎 | 订单簿管理 | 库存管理 | 波动率计算 数据流方向:接收行情 → 计算报价 → 发送订单请求 消息流:事件总线(异步非阻塞) 执行层(Execution Layer) 订单管理 | 路由分发 | 撤单重发 | 成交回报处理 数据流方向:订单请求 → 交易所 → 成交回报 关键指标:延迟 < 100μs,丢单率 < 0.01% 风控层(Risk Control Layer) 资金检查 | 仓位限制 | 报价频率控制 | 异常熔断 数据流方向:拦截违规报价,触发告警/熔断 日志与监控模块(横切关注点) 异步日志 | 延迟监控 | 丢单告警 | 性能指标采集 日志流 监控流 数据流:实线箭头 | 消息流:事件总线(内部) | 日志/监控:虚线

这张图里,我特意把日志与监控模块放在最下面,用虚线连接到各层。为什么?因为它是横切关注点,不参与业务逻辑,但每一层都离不开它。说白了,没有监控的报价引擎,就像闭着眼睛开车——你根本不知道下一秒会撞上什么。

六、架构设计中的几个关键决策

最后,我想聊聊几个架构设计中的关键决策。这些决策没有绝对的对错,但会影响系统的方方面面。

决策1:单进程 vs 多进程?

我建议核心链路用单进程(多线程),非核心链路用多进程。为什么?因为单进程内的通信延迟最低(纳秒级),而多进程间的IPC至少是微秒级。但单进程有个问题:一个线程挂了,整个进程就挂了。所以风控模块一定要独立成进程,即使主进程挂了,风控还能把仓位平掉。

决策2:同步 vs 异步?

报价引擎里,所有IO操作都必须是异步的。同步IO会阻塞线程,导致后续报价延迟。我见过有人用同步HTTP去查行情,结果一次查询花了50毫秒,这期间系统完全停摆。换成异步后,延迟降到了1毫秒以下。

决策3:内存数据库 vs 关系型数据库?

运行时数据(订单簿、仓位)用内存数据库(如Redis),持久化数据(交易记录、日志)用关系型数据库。千万别把运行时数据放MySQL,否则一次慢查询就能让你的报价引擎「卡死」。我曾经犯过这个错,那次事故让我明白了:数据库是给报表用的,不是给交易用的

好了,架构总览就聊到这里。记住一句话:好的架构不是设计出来的,是改出来的。先跑通一个最小可行版本,然后根据实际运行数据不断优化。别一开始就想搞个完美的架构,那只会让你陷入「过度设计」的泥潭。


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