第二章:核心技术栈选型

做市商系统的技术选型,说白了就是一场权衡游戏。我做了这么多年量化系统,见过太多团队在选型上栽跟头——要么选了个大炮打蚊子,要么选了个玩具扛不住压力。今天我就把这几年的经验摊开来讲讲。

2.1 编程语言:C++、Java、Go 的三国杀

先说说语言选型。这个问题我几乎每次技术评审都会被问到。我的回答很简单:看你的核心诉求是什么

C++:极致性能的代价

如果你要做高频做市,延迟要求在微秒级别,那 C++ 几乎是唯一选择。我在项目中遇到过用 C++ 实现的订单簿,单笔订单处理能做到 500 纳秒以内。但代价是什么?开发效率低,内存管理稍有不慎就崩给你看。

我曾经... 接手过一个 C++ 做市系统,前任团队为了追求极致性能,用了大量手写内存池。结果上线第三天就出现内存泄漏,排查了整整一周才发现是某个回调函数里忘了释放资源。嗯,从那以后我定了个规矩:非核心链路不要用 C++。

Java:生态为王

Java 是我个人最推荐的中低频做市系统首选。为什么?生态太成熟了。Netty、Disruptor、Kafka 客户端,随便拎出来一个都是久经考验的组件。而且 JVM 的 GC 调优,现在 ZGC 已经能把暂停时间控制在 1ms 以内,对大部分做市场景完全够用。

我的建议: 如果你的系统延迟要求是毫秒级,Java 是最稳妥的选择。团队招人也容易,市面上 Java 工程师一抓一大把。

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 分钟,期间整个系统不可用。后来我们不得不重新设计分区策略。

避坑指南: 我曾经在 Kafka 上吃过亏——分区数超过 200 后,性能会急剧下降。建议控制在 100 个以内,如果不够用,考虑多集群方案。

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 的混合模式,才解决了这个问题。

核心要点: Redis 只存热数据,而且要做好主从切换。别指望它做持久化存储,那是 ClickHouse 的事。

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 参数就花了两周。

我的经验: Netty 的 EventLoop 线程数不要超过 CPU 核心数的两倍。我曾经设了 32 个线程,结果上下文切换开销比业务处理还大。

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(冷数据:历史成交/日志) Java / Go / C++

这张图展示的是我推荐的标准架构。从上到下依次是:Netty 负责外部接入,gRPC 串联内部服务,Kafka/Pulsar 做消息解耦,Redis 和 ClickHouse 各司其职。每一层都有明确的职责边界,这样出了问题也好排查。

最后说一句: 技术选型没有银弹。我见过用 C++ 写全栈的团队,也见过全用 Java 搞定一切的。关键是搞清楚你的业务场景,然后选择最合适的工具。别为了炫技而选型,那是给自己挖坑。
专注资料整理