3、自动化系统架构设计:分层架构、微服务与消息队列
做市业务的清算结算,说白了就是一场与时间赛跑的「对账游戏」。
我入行那会儿,系统还是单体架构。每天凌晨跑批,十几个模块挤在一个进程里。哪个环节挂了,整个清算就卡死。运维同事半夜打电话叫醒我,说「哥,又堵了」——那种感觉,真不想再体验第二次。
所以后来设计自动化系统,我脑子里就三个关键词:解耦、弹性、可观测。今天咱们就聊聊,怎么用分层架构、微服务和消息队列把这三点落地。
3.1 分层架构:各司其职,别越界
我习惯把系统切成三层。每层只干自己的活,不越界。这样出了问题,定位也快。
核心原则:上层依赖下层,下层不依赖上层。数据流单向流动。
3.1.1 数据层
这一层负责「存」和「取」。说白了,就是跟数据库、缓存、文件系统打交道。
- 关系型数据库:存交易流水、持仓、账户余额。我一般用 MySQL 或 PostgreSQL,分库分表是常态。
- 缓存:Redis 扛高频查询。比如实时持仓快照,放 Redis 里,别每次都查库。
- 消息持久化:清算过程中的中间状态,我习惯写进消息队列的持久化日志里。万一崩了,能从断点续传。
嗯,这里要注意:数据层的设计一定要考虑「幂等性」。我曾经因为重复消费导致持仓多算了一倍,排查了整整一个通宵。后来所有写入操作都加了唯一键约束,才彻底解决。
3.1.2 逻辑层
这是核心。清算规则、费用计算、净额轧差、风险校验……全在这层。
我个人的习惯是:把每个业务规则封装成独立的「处理器」。比如「计算交易费用」是一个处理器,「计算持仓盈亏」是另一个。它们之间通过事件驱动,互不干扰。
举个例子:
// 伪代码:清算规则链
public class SettlementRuleChain {
private List<RuleProcessor> processors;
public void execute(SettlementContext context) {
for (RuleProcessor processor : processors) {
if (!processor.process(context)) {
// 某个规则失败,终止清算
log.error("规则 {} 执行失败", processor.getName());
break;
}
}
}
}
你想想看,如果所有规则写在一个大方法里,改一个地方就得全量回归测试。拆成链式调用,新增规则只需要加一个处理器,多清爽。
3.1.3 展示层
这一层是给运营和风控同事看的。我见过太多系统,后端逻辑写得漂亮,前端却一团糟——数据对不上、刷新不及时、操作路径反人类。
我的经验是:展示层只做「呈现」和「交互」,不做任何业务计算。
- 清算进度实时展示:用 WebSocket 推数据,别让用户手动刷新。
- 异常告警高亮:红色标出,点击后直接跳转到对应的日志或数据详情。
- 操作留痕:谁在什么时间做了什么操作,全部记录。方便事后审计。
小技巧:展示层的数据模型,最好跟后端的数据模型分开。前端需要的是「展示视图」,后端给的是「业务实体」。中间加一层 DTO 转换,能避免很多耦合问题。
3.2 微服务设计:拆得合理,合得优雅
微服务不是拆得越碎越好。拆得太细,服务间通信成本高得吓人;拆得太粗,又回到单体老路。
我一般按「业务边界」来拆。做市清算结算,我拆成这几个服务:
| 服务名称 | 职责 | 数据存储 |
|---|---|---|
| 交易接入服务 | 接收交易所/对手方的成交回报 | MySQL + Redis |
| 清算引擎服务 | 净额轧差、费用计算、持仓更新 | MySQL + 消息队列 |
| 结算对账服务 | 与资金托管行/中央对手方对账 | MySQL + 文件存储 |
| 风控校验服务 | 实时校验清算结果是否超限 | Redis + 时序数据库 |
| 通知服务 | 推送清算结果给运营、风控、监管 | 消息队列 + 邮件/短信网关 |
每个服务独立部署、独立数据库。服务间通过 gRPC 或 RESTful API 通信。我个人偏爱 gRPC,性能好,而且强类型接口不容易出错。
避坑指南:我曾经把一个「清算引擎」拆成了「净额计算服务」和「费用计算服务」。结果两个服务需要频繁交换中间数据,网络开销比计算本身还大。后来合并成一个服务,反而更快。所以,高频交互的模块,别硬拆。
3.3 消息队列选型:选对了,事半功倍
消息队列在清算系统里,主要干三件事:
- 异步解耦:交易接入后,直接扔进队列,清算引擎慢慢消费。
- 削峰填谷:盘后清算高峰,队列能缓冲压力。
- 可靠投递:确保每条交易都不丢、不重复。
市面上主流的消息队列,我基本都用过。说说我的感受:
| 选型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Kafka | 高吞吐、持久化、分区有序 | 运维复杂、延迟稍高 | 大规模交易流水、日志采集 |
| RabbitMQ | 灵活的路由、低延迟、易运维 | 吞吐量不如 Kafka | 实时通知、任务分发 |
| RocketMQ | 事务消息、延迟消息、金融级可靠 | 社区生态不如前两者 | 清算结算、对账、资金划拨 |
| Pulsar | 存算分离、多租户、云原生 | 学习曲线陡 | 大型金融云平台 |
我个人在清算场景里,最常用的是 RocketMQ。为什么?因为它的事务消息机制,能保证「本地事务」和「消息发送」的原子性。说白了,就是要么都成功,要么都回滚。这对资金类业务太重要了。
举个例子:
// 伪代码:事务消息示例
public void settleTrade(Trade trade) {
// 1. 本地事务:更新数据库
updateSettlementStatus(trade);
// 2. 发送事务消息
TransactionMQProducer producer = ...;
Message msg = new Message("settle_topic", trade.toBytes());
producer.sendMessageInTransaction(msg, null);
}
// 3. 消息消费者处理
public void onMessage(Message msg) {
Trade trade = parseTrade(msg);
// 执行后续清算逻辑
calculateFee(trade);
updatePosition(trade);
}
嗯,这里要提醒一句:消息队列不是银弹。如果业务逻辑强依赖顺序,比如「先减持仓、再加资金」,那就要用分区有序的队列(比如 Kafka 的 partition 或 RocketMQ 的 MessageQueue)。否则乱序消费会导致数据不一致。
3.4 架构总览:一张图说清楚
下面这张图,是我做清算系统架构设计时常用的分层结构。你可以看到数据从交易所流入,经过各层处理,最终输出清算结果。
你看,数据从下往上流,展示层只负责呈现。逻辑层通过消息队列与数据层解耦。每一层都能独立扩缩容——这就是分层架构的魅力。
最后说一句:架构设计没有标准答案。我给你的这套方案,是在多个做市商项目里打磨出来的。但你的业务场景可能不同,比如交易量级、合规要求、团队规模……这些都会影响最终选型。灵活调整,别硬套。
公众号:蓝海资料掘金营,微信deep3321