一、系统架构总览:多节点交易系统的整体架构、核心组件与数据流
大家好,我是你们的运维架构师。今天咱们聊聊多节点交易系统的架构总览。说实话,我最早接触交易系统时,也被那一堆组件搞得头晕。后来亲手搭过几套生产环境,才慢慢摸清门道。
先看一张整体架构图,我习惯用这种分层结构来理解系统:
1.1 整体架构分层
多节点交易系统,说白了就是一套「拆开干」的架构。把不同职责拆到独立服务里,每个服务可以单独扩缩容。我最早在创业公司做交易系统时,所有功能揉在一个进程里,上线后一崩全崩,那叫一个惨。
这套架构通常分四层:
- 接入层:负责跟用户打交道。接收订单、推送行情、做限流和认证。
- 核心业务层:这是心脏。撮合、风控、清算、行情生成,都在这里。
- 数据层:存订单、存账户、做缓存、走消息队列。
- 监控运维层:盯着系统别出事,出事赶紧报警。
嗯,这里要注意:层与层之间必须解耦。我见过一个项目,撮合引擎直接连数据库写订单,结果数据库一慢,撮合也跟着卡死。后来改成异步写,问题就解决了。
1.2 核心组件详解
1.2.1 网关(Gateway)
网关是系统的「门卫」。用户的所有请求先进网关,再由网关转发到后端。
网关主要干这几件事:
- 协议转换:用户可能用HTTP、WebSocket、甚至自定义TCP协议进来。网关统一转成内部RPC协议。
- 限流熔断:防止某个用户刷单把系统打垮。我习惯用令牌桶算法,配合Redis做分布式限流。
- 身份认证:验签名、验Token。别让非法请求进到核心层。
- 会话管理:维护用户的长连接,推送成交回报和行情。
避坑指南:我曾经在生产环境遇到网关超时导致订单重复提交的问题。原因是网关重试机制没做好幂等。后来在网关层加了请求去重表(用Redis的SETNX),同一个订单号5秒内只处理一次。
1.2.2 撮合引擎(Matching Engine)
撮合引擎是交易系统的灵魂。说白了,就是把买单和卖单按价格优先、时间优先的原则配对。
我个人习惯把撮合引擎设计成无状态服务。为什么?因为好扩容。订单进来后,按交易对(比如BTC/USDT)做哈希,路由到对应的撮合实例上。
撮合引擎的核心逻辑:
// 伪代码:撮合核心逻辑
function matchOrder(newOrder, orderBook):
while newOrder.remainingQty > 0:
// 取对手盘最优价格
bestOrder = orderBook.getBestOpposite(newOrder.side)
if bestOrder is null:
break // 没有对手盘,挂单
// 检查价格是否匹配
if newOrder.side == BUY and newOrder.price < bestOrder.price:
break // 买价低于卖价,不成交
// 计算成交数量
tradeQty = min(newOrder.remainingQty, bestOrder.remainingQty)
tradePrice = bestOrder.price // 以挂单价格成交
// 生成成交记录
emitTrade(newOrder, bestOrder, tradeQty, tradePrice)
// 更新剩余数量
newOrder.remainingQty -= tradeQty
bestOrder.remainingQty -= tradeQty
// 如果对手盘完全成交,从订单簿移除
if bestOrder.remainingQty == 0:
orderBook.remove(bestOrder)
// 如果新订单还有剩余,挂入订单簿
if newOrder.remainingQty > 0:
orderBook.add(newOrder)
你想想看,撮合引擎最怕什么?最怕慢。一秒几万笔订单进来,如果撮合逻辑里有数据库操作,那肯定扛不住。所以撮合引擎通常全内存运行,订单簿用红黑树或跳表实现。
1.2.3 风控引擎(Risk Control)
风控引擎是系统的「刹车」。没有风控的交易系统,就像没有刹车的跑车——早晚出事。
风控检查通常在两个阶段触发:
- 下单前检查:用户账户余额够不够?持仓上限超没超?今天交易次数是不是太多了?
- 成交后检查:成交后账户是否异常?有没有自成交嫌疑?
个人经验:我建议风控规则做成可配置的,别写死在代码里。用规则引擎(比如Drools)或者干脆存数据库里,运营人员可以动态调整。有一次交易所被黑客利用API刷单,就是因为风控阈值写死了,改不了。
1.2.4 清算系统(Clearing)
清算系统负责「算账」。每天交易结束后,算清楚每个用户赚了多少、亏了多少,该给谁转钱。
清算的核心流程:
| 步骤 | 说明 | 注意事项 |
|---|---|---|
| 1. 交易对账 | 核对撮合引擎产生的成交记录与数据库是否一致 | 我曾经遇到过撮合引擎内存数据丢失,导致成交记录对不上。后来加了WAL日志才解决。 |
| 2. 资金计算 | 计算每个用户的应收应付、手续费、保证金 | 精度问题要小心,建议用decimal类型,别用float |
| 3. 账户更新 | 批量更新用户账户余额和持仓 | 用乐观锁防止并发更新冲突 |
| 4. 生成报表 | 生成日结报表、对账单 | PDF和CSV格式都要支持 |
1.3 数据流与订单生命周期
一条订单从用户发起到最终成交或撤销,经历了什么?我画个流程图你就明白了。
订单的生命周期,我总结为七个关键节点:
- 用户下单:客户端发起请求,带上交易对、方向、价格、数量。
- 网关校验:检查参数合法性、验签、限流。不合法的直接打回。
- 风控检查:查余额、查持仓、查频率。风控不通过,订单直接拒绝。
- 进入队列:通过风控的订单进入消息队列,保证不丢不重。
- 撮合引擎处理:从队列消费订单,尝试撮合。撮合成功生成成交记录,撮合失败则挂单。
- 清算更新:成交后异步更新账户余额和持仓。
- 结果推送:通过WebSocket或长连接,把成交回报推给用户。
警告:订单生命周期中,第4步「进入队列」和第5步「撮合引擎处理」之间,最容易出现数据不一致。我建议用「至少一次投递 + 幂等消费」的模式。消息队列选Kafka或RocketMQ,别用RabbitMQ——吞吐量扛不住交易场景。
1.4 多节点部署的关键考量
多节点不是简单地把服务复制几份就完事了。有几个坑我得提醒你:
- 状态同步:撮合引擎虽然是内存运行,但订单簿状态需要持久化。我习惯用Redis做状态快照,配合Raft协议做选主。
- 数据一致性:多节点下,同一个用户的订单可能落在不同节点。用一致性哈希做路由,保证同一个用户的订单始终路由到同一个撮合节点。
- 网络延迟:节点间通信延迟不能超过1ms。我建议撮合节点部署在同一台物理机或同一个机架内,用RDMA网络。
- 容灾切换:主节点挂了,从节点要秒级接管。Keepalived + VIP 或者 Consul 做服务发现都可以。
个人习惯:我每次部署多节点系统,都会先做一次「混沌工程」演练——随机杀掉一个节点,看看系统能不能自动恢复。第一次演练时,发现风控引擎的本地缓存没做同步,切节点后风控规则丢了。后来改成Redis集中缓存,问题解决。
好了,这一章就聊到这儿。架构总览是地基,地基打牢了,后面讲部署、调优、监控才能站得住脚。下一章咱们深入网关层,聊聊怎么扛住百万并发。
公众号:蓝海资料掘金营,微信deep3321