第二章:核心技术栈选型
做市商系统的技术选型,说白了就是一场权衡游戏。我做了这么多年量化系统,见过太多团队在选型上栽跟头——要么选了个大炮打蚊子,要么选了个玩具扛不住压力。今天我就把这几年的经验摊开来讲讲。
2.1 编程语言:C++、Java、Go 的三国杀
先说说语言选型。这个问题我几乎每次技术评审都会被问到。我的回答很简单:看你的核心诉求是什么。
C++:极致性能的代价
如果你要做高频做市,延迟要求在微秒级别,那 C++ 几乎是唯一选择。我在项目中遇到过用 C++ 实现的订单簿,单笔订单处理能做到 500 纳秒以内。但代价是什么?开发效率低,内存管理稍有不慎就崩给你看。
Java:生态为王
Java 是我个人最推荐的中低频做市系统首选。为什么?生态太成熟了。Netty、Disruptor、Kafka 客户端,随便拎出来一个都是久经考验的组件。而且 JVM 的 GC 调优,现在 ZGC 已经能把暂停时间控制在 1ms 以内,对大部分做市场景完全够用。
Go:后起之秀
Go 语言最近几年在量化圈越来越火。我去年帮一个客户重构做市系统,他们原来用 Java,后来全切成了 Go。原因很简单:协程太好用了。做市系统里大量并发的订单管理、行情处理,Go 的 goroutine 写起来比 Java 的线程池优雅太多。
但 Go 也有短板。它的泛型支持刚成熟,生态相比 Java 还是差一截。你想想看,Java 的 Netty 社区有多少轮子?Go 这边还得自己造。
| 维度 | C++ | Java | Go |
|---|---|---|---|
| 延迟 | 纳秒级 | 毫秒级 | 微秒级 |
| 开发效率 | 低 | 高 | 中高 |
| 生态成熟度 | 中 | 极高 | 中 |
| 团队招聘 | 难 | 易 | 中 |
| 适用场景 | 高频做市 | 中低频做市 | 中高频做市 |
2.2 消息中间件:Kafka 还是 Pulsar?
消息中间件这块,我踩过的坑最多。做市系统里,行情数据、订单状态、风控事件,全都要靠消息队列来解耦。选错了,后面重构的成本高得吓人。
Kafka:老牌劲旅
Kafka 是我用得最多的消息中间件。它的吞吐量确实能打,单机百万条消息每秒不是吹的。而且生态极其完善,各种流处理框架都支持 Kafka 作为数据源。
但 Kafka 有个问题:分区数不能太多。我记得有个项目,为了做多租户隔离,分了 1000 个分区。结果呢?Rebalance 一次要 5 分钟,期间整个系统不可用。后来我们不得不重新设计分区策略。
Pulsar:新贵登场
Pulsar 是后来者,但它的设计理念我很欣赏。它把存储和计算分离了,这样扩缩容就灵活很多。而且支持多租户、分层存储,这些对做市系统来说都是刚需。
不过 Pulsar 的社区活跃度还是不如 Kafka。我去年用 Pulsar 做了一套行情分发系统,遇到一个消费者组管理的 bug,查了三天才发现是版本兼容问题。嗯,这就是生态不成熟的代价。
| 特性 | Kafka | Pulsar |
|---|---|---|
| 吞吐量 | 极高 | 高 |
| 延迟 | 毫秒级 | 毫秒级 |
| 多租户 | 需手动实现 | 原生支持 |
| 运维复杂度 | 中 | 高 |
| 社区活跃度 | 极高 | 中 |
2.3 数据库选型:Redis 与 ClickHouse 的黄金搭档
做市系统的数据存储,我一般分两层:热数据用 Redis,冷数据用 ClickHouse。为什么这么分?你想想看,订单簿的实时状态需要微秒级读写,而历史成交数据需要做大规模分析,这两者的需求完全不同。
Redis:内存里的闪电战
Redis 在量化系统里几乎是标配。我习惯用它来存订单簿快照、账户余额、风控阈值。它的数据结构丰富,Sorted Set 做订单队列、Hash 做账户信息,用起来很顺手。
但 Redis 有个坑:持久化要小心。我曾经遇到过一次 Redis 宕机,因为 RDB 快照太大,恢复花了 10 分钟。那 10 分钟里,整个做市系统都在裸奔。后来我们改成了 AOF + 定期 RDB 的混合模式,才解决了这个问题。
ClickHouse:分析型数据库的王者
ClickHouse 是我做量化系统以来用过的最爽的分析型数据库。它的列式存储和向量化执行引擎,让历史数据的聚合查询快得离谱。我们之前用 MySQL 查一天的逐笔成交,要 30 秒;换成 ClickHouse 后,同样的查询只要 200 毫秒。
不过 ClickHouse 不适合做事务性操作。它的更新和删除性能很差,我建议只用来存不可变的历史数据,比如成交记录、行情快照。
| 场景 | 推荐数据库 | 原因 |
|---|---|---|
| 订单簿实时状态 | Redis | 微秒级读写 |
| 账户余额 | Redis | 高并发访问 |
| 历史成交记录 | ClickHouse | 大规模分析 |
| 风控规则 | Redis | 低延迟匹配 |
| 日志审计 | ClickHouse | 高吞吐写入 |
2.4 网络框架:Netty 与 gRPC 的抉择
网络通信是做市系统的命脉。选错了框架,延迟和吞吐量都会受影响。我个人习惯:内部服务用 gRPC,外部接入用 Netty。
Netty:Java 网络编程的基石
Netty 我用了快十年了。它的 Reactor 模型和零拷贝技术,让 Java 也能写出高性能的网络应用。做市系统里,行情网关、交易网关这些对外接口,我都是用 Netty 来实现的。
Netty 的好处是灵活,你可以自己控制编解码、流量整形、连接管理。但坏处也是太灵活——配置项多到让人头疼。我记得刚开始用 Netty 时,光是调优 TCP 参数就花了两周。
gRPC:微服务通信的首选
gRPC 是 Google 开源的 RPC 框架,基于 HTTP/2 和 Protobuf。我推荐用它来做内部服务之间的通信,比如订单管理服务和风控服务之间的调用。它的双向流特性,很适合做市系统里的实时数据推送。
但 gRPC 有个问题:调试不方便。Protobuf 序列化后的数据是二进制的,不像 JSON 那样可以直接看。我建议在开发环境保留一个 JSON 接口,方便排查问题。
| 维度 | Netty | gRPC |
|---|---|---|
| 协议 | 自定义 TCP | HTTP/2 |
| 序列化 | 自定义 | Protobuf |
| 适用场景 | 外部接入 | 内部服务 |
| 延迟 | 极低 | 低 |
| 开发效率 | 中 | 高 |
2.5 技术栈全景图
说了这么多,我把整个技术栈的架构画成了一张图。你一看就明白了。
这张图展示的是我推荐的标准架构。从上到下依次是:Netty 负责外部接入,gRPC 串联内部服务,Kafka/Pulsar 做消息解耦,Redis 和 ClickHouse 各司其职。每一层都有明确的职责边界,这样出了问题也好排查。