二、系统架构总览:整体架构分层设计、核心模块划分、技术选型原则、架构设计目标
好,咱们正式开始聊架构。很多朋友一上来就问我:“做市系统到底长什么样?” 我的回答通常是:别急着看代码,先看分层。分层搞明白了,后面所有细节都能对号入座。
我个人习惯把债券做市系统拆成四层。为什么是四层?因为我在第一版设计时只分了“接入”和“业务”两层,结果上线后耦合得一塌糊涂,改一个报价逻辑差点把网关搞崩。后来痛定思痛,才有了今天这套分层方案。
2.1 整体架构分层设计
先看这张图,这是我手绘的架构分层,你感受一下:
这张图我画了很多遍,每次讲课时都会拿出来。你注意看,每一层我都标注了延迟目标。为什么?因为做市系统对延迟极度敏感,每一层多花10微秒,到客户端可能就是一笔交易都抢不到。
核心原则:层与层之间通过内存队列或共享内存通信,避免跨网络调用。我见过有人把行情数据从接入层先写到数据库,再让业务层去读——结果延迟直接飙到毫秒级,这种设计在债券做市里是行不通的。
2.2 核心模块划分
分层之后,咱们看看每一层里到底放了哪些模块。我按模块的重要程度排个序:
2.2.1 接入层模块
- FIX 网关:处理 FIX 4.4/5.0 协议,支持会话恢复。我记得有一次交易所升级协议版本,我们的网关因为没做版本协商,直接断连了半小时。后来我加了个协议版本自动探测机制,再也没出过问题。
- WebSocket 网关:给内部交易员用的,支持订阅式行情推送。
- HTTP REST 网关:管理类操作,比如查询持仓、修改参数。这个路径不追求极致延迟,但要求高可用。
- 流量整形器:防止恶意高频请求打垮后端。我曾经遇到一个客户,每秒发 10 万笔报价请求,差点把系统搞挂。后来加了令牌桶限流,才稳住。
2.2.2 核心业务层模块
- 报价引擎:这是心脏。接收行情,计算双边报价,发送给交易所。内部用事件驱动架构,每个债券的报价计算在一个独立的 Actor 里完成。
- 风控引擎:检查每笔报价和成交是否越界。包括:最大持仓、最大名义本金、单笔最小/最大量、价格偏离度等。风控必须前置,不能等成交了再检查。
- 订单管理器:维护所有未成交订单的状态机。支持撤单、改单、部分成交等场景。
- 做市策略模块:这是各家券商的“黑盒”。我见过用纯规则引擎的,也见过用强化学习的。但不管用什么,策略模块必须支持热加载,不能因为改参数就重启系统。
- 簿记管理:实时记录每笔成交,计算净头寸。这个模块的数据一致性要求极高,我建议用乐观锁 + 版本号机制。
2.2.3 数据服务层模块
- 行情缓存:用 Redis Cluster 存最新行情快照。注意,这里不能用 Redis 的持久化功能,会拖慢性能。
- 历史数据存储:用 ClickHouse 或 TimescaleDB 存 tick 级数据,用于回测和复盘。
- 参考数据管理:债券基本信息、发行人评级、久期、凸性等。这些数据变化不频繁,但查询量大,建议用本地内存缓存。
2.2.4 基础设施层模块
- 消息队列:Kafka 用于异步事件,比如成交确认、风控告警。Pulsar 用于跨机房复制。
- 服务发现:Consul 管理所有微服务的注册与健康检查。
- 监控告警:Prometheus 采集指标,Grafana 展示。告警规则要细化到每个模块的延迟 P99。
我的经验:模块划分时,一定要遵循“高内聚、低耦合”原则。我见过一个系统把报价计算和订单管理放在同一个进程里,结果报价逻辑出 bug 导致整个订单处理阻塞。后来拆成两个独立进程,用共享内存通信,问题就解决了。
2.3 技术选型原则
技术选型这事儿,我踩过的坑比走过的路还多。总结下来,就三条原则:
| 原则 | 说明 | 我的案例 |
|---|---|---|
| 延迟优先 | 能用 C++/Rust 就不用 Java,能用 Java 就不用 Python。核心路径上禁止 GC 停顿。 | 我曾经用 Java 写报价引擎,结果 Full GC 导致报价中断了 2 秒,被交易所罚款。后来全部重写为 C++,延迟稳定在 5μs 以内。 |
| 简单至上 | 能用单机解决就别上分布式。能用内存解决就别用数据库。 | 有个团队非要用 Cassandra 存行情快照,结果延迟比 Redis 高了一个数量级。我直接让他们换成 Redis Cluster,性能提升 10 倍。 |
| 可观测性 | 每个模块必须暴露延迟、吞吐量、错误率三个指标。没有监控的模块,等于没上线。 | 我接手过一个系统,没有任何日志和指标。出问题时全靠猜。后来我强制要求每个接口都打日志,并接入 Prometheus,排查效率提升 80%。 |
避坑指南:我曾经选过一个“看起来很酷”的分布式数据库,结果运维成本极高,而且延迟不稳定。后来我换回了单机 Redis + 本地磁盘持久化,反而更可靠。记住:做市系统不是炫技场,稳定压倒一切。
2.4 架构设计目标
聊完分层和选型,咱们得明确目标。没有目标的架构,就像没有地图的航行。我总结为三个关键词:
2.4.1 低延迟
债券做市对延迟的要求有多苛刻?这么说吧,交易所的撮合周期是 100 微秒。如果你的报价比竞争对手晚到 50 微秒,这笔交易就跟你没关系了。所以,我们的目标是:
- 接入层到核心业务层:< 20μs
- 报价计算完成:< 50μs
- 全链路端到端:< 200μs
怎么实现?说白了就是:减少拷贝、避免锁、用无锁队列、CPU 亲和性绑定。我习惯把核心线程绑定到独立 CPU 核心上,避免上下文切换。
2.4.2 高可用
做市系统不能停。停了就是真金白银的损失。我要求系统达到 99.999% 的可用性,也就是一年宕机时间不超过 5 分钟。怎么做到?
- 多活部署:至少两个机房同时提供服务,一个挂了另一个秒级接管。
- 无状态设计:所有业务模块都是无状态的,状态统一放在 Redis 或共享内存里。
- 优雅降级:行情源断了怎么办?风控模块挂了怎么办?每个模块都要有降级方案。我遇到过行情源中断 10 分钟的情况,系统自动切换到备行情源,交易员完全无感知。
2.4.3 可扩展
债券市场在变,监管在变,策略在变。架构必须能快速适应变化。我的原则是:
- 插件化策略:每个做市策略是一个独立的插件,可以热加载、热卸载。
- 配置驱动:所有业务参数(报价价差、最大持仓、风控阈值)都通过配置中心下发,不需要改代码。
- 水平扩展:当债券数量从 1000 只增长到 10000 只时,系统能通过加机器线性扩展。我建议用一致性哈希做分片,每个分片负责一部分债券。
总结一下:低延迟是生存之本,高可用是底线,可扩展是未来。这三个目标缺一不可。如果你问我哪个最重要?我会说:低延迟。因为做市系统如果慢了,连生存的机会都没有,还谈什么高可用和可扩展?
嗯,架构总览就聊到这儿。下一节咱们深入报价引擎,看看那个“心脏”到底是怎么跳动的。
公众号:蓝海资料掘金营,微信deep3321