参考架构
示意高性能交易平台
一个面向金融交易的微服务平台,基于 Java、Spring Boot、Kafka、Redis 和关系型账本构建,设计上正确性第一,吞吐量第二。
问题
设想一个平台,它从 Web、移动端和合作方渠道接收支付或转账指令,并且必须把每一笔恰好记录一次。业务量并不均匀,在发薪日、促销活动和结算截止时点前后会出现陡峭的峰值。下游网络和合作方系统比平台自身被期望达到的水平更慢,也更不可靠。
难点不在于纯粹的速度,而在于当客户端重试、消息重复到达、节点在交易中途故障、外部方超时却不说明自己是否已经执行时,仍然保持正确性。一个在压测中表现出色、却解释不了一分钱去向的设计,是不合格的。
设计必须满足的需求
- 从客户的角度看,一笔交易必须恰好生效一次,即使客户端或内部组件发生了重试。
- 账本必须始终保持平衡,每一笔资金变动都记录为不可变的复式分录。
- 平台必须通过水平扩展吸收峰值负载,既不需要重新设计,也不降低余额的一致性。
- 任何单个服务、节点或可用区的故障,都不得丢失一笔已受理的交易。
- 每一次状态变更都必须可审计,内部记录必须与外部结算文件进行对账。
- 运维人员必须能够近实时地看到延迟、错误率、队列深度和业务异常。
设计
组件
渠道
- Web
- 移动端
- 合作方 API
- 批量文件
API 网关
- 身份认证
- 限流
- 幂等键
- 请求签名
交易服务
Java 与 Spring Boot,无状态,水平扩展
- 支付发起
- 授权
- 账本记账
- 限额与风控
Kafka 事件主干
- 交易事件
- Outbox 中继
- 分区主题
- 死信主题
账本数据库与 Redis
账本是权威数据,缓存不是
- 复式记账账本
- 只读副本
- 余额缓存
- 分布式锁
对账与审计
- 结算匹配
- 差异检测
- 不可变审计日志
- 报表导出
监控
- 延迟与错误率 SLO
- 消费者滞后
- 链路追踪
- 业务告警
API 网关
网关负责终止 TLS、认证调用方、按客户端限流,并校验合作方流量的请求签名。它要求每一个会改变状态的请求都携带幂等键,并将其传递给下游。网关之后的任何组件都无法从公网直接访问。
交易服务
无状态的 Spring Boot 服务分别处理发起、授权、限额和记账,各自处在自己的限界上下文内。一笔交易沿着显式的状态机流转,因此它的状态始终是少数几个已知取值之一。这些服务在网关之后水平扩展,不持有任何会在重启时丢失的状态。
账本数据库
关系型数据库以只追加的分录形式保存复式账本,余额由分录推导得出。ACID 事务和约束在提交时强制保证借贷相等。只读副本承担对账单和报表查询,使分析类查询不与写入路径争抢资源。
Kafka 事件主干
每个服务在同一个数据库事务中写入状态变更和一条 Outbox 记录,再由中继程序把 Outbox 发布到 Kafka。主题按账户或交易键分区,在需要的地方保证顺序。消费者是幂等的,反复失败的事件会被路由到死信主题以供排查。
Redis 缓存与协调
Redis 保存幂等记录、限流计数器,以及限额和参考数据这类短时缓存的读取结果。它被当作加速器,而绝不是记录系统。如果 Redis 不可用,平台会变慢,但余额依然正确,因为余额是从账本读取的。
对账与审计
对账服务把内部分录与结算文件和合作方文件逐笔匹配,对任何未匹配或不一致的记录生成差异项。差异项通过运营队列处理,并保留完整的处理历史。不可变的审计日志记录了每笔交易由谁、由什么、在何时变更。
云基础设施
服务以容器形式运行在跨多个可用区的 Kubernetes 上,数据库采用主库加同步备库的部署方式。基础设施用 Terraform 定义,部署采用渐进式发布,有问题的版本只会先触达一小部分流量。备份会按计划执行恢复,以证明恢复确实可行。
监控与告警
指标、日志和分布式追踪通过交易标识从网关一路关联到账本。服务等级目标覆盖延迟和错误率,Kafka 消费者滞后则作为故障的早期信号受到关注。业务告警,例如异常的拒绝率或不断增长的对账积压,与技术告警并列呈现。
取舍
设计决策
| 决策 | 理由 |
|---|---|
| 以关系型数据库作为权威账本 | 在这里,强一致性和事务约束比最终一致性存储的写入扩展能力更有价值。代价是账本会成为扩展上的约束,应对方式是保持事务短小、必要时按账户区间分区,并把读取转移到副本。 |
| 用事务性 Outbox 取代双写 | 分别写入数据库和 Kafka 会留下一个窗口:一方成功而另一方失败。Outbox 增加了一个中继组件和少量发布延迟,换来的保证是:每一个已提交的变更最终都会发布,未提交的变更永远不会发布。 |
| 在网关执行一次幂等校验,在账本再执行一次 | 客户端和网络都会重试,至少一次的消息投递也会带来重复。在边缘检查幂等键可以低成本地处理常见情况,而账本中的唯一约束是最后一道防线,即使缓存丢失也依然有效。 |
| 授权同步,其余一律异步 | 客户需要立即得到一个明确的答复:交易是否已被受理,因此这条路径是同步的,并保持简短。通知、报表、风险评分更新和结算由事件驱动,这使它们不占用延迟预算,并且可以各自独立地失败和恢复。 |
| 按业务能力而不是按技术分层拆分服务 | 微服务会带来运维成本:更多的部署、更多的网络调用和更困难的调试。只有当各项能力的扩展节奏或变更节奏不同时,拆分才有道理,因此设计从少量服务起步,没有具体理由就不再继续拆分。 |
