调查研究-195 从 AmEx 支付系统看 Cell-based Architecture:真正的高可用,不是无限重试,而是控制失败边界

从 AmEx 支付系统看 Cell-based Architecture:真正的高可用,不是无限重试,而是控制失败边界

TL;DR

  • 场景:高可用支付系统的架构设计,AmEx 2018 年起现代化核心支付生态
  • 结论:高可用的核心不是让组件永不失败,而是用 Cell + GTR 把失败控制在边界内
  • 产出:12 节工程实践 + 5 张配套图示 + 错误速查卡

版本矩阵

功能/特性 状态 说明
Cell-based Architecture 用于核心支付 ✅ 已验证 AmEx 2018 年起现代化核心支付生态,每个核心支付生态实例即一个 Cell(来源:americanexpress.io
每个 Cell 独立部署、拥有独立数据 ✅ 已验证 来源:AmEx 文章原文
Cell 在交易关键路径上无同步跨 Cell 依赖 ✅ 已验证 AmEx 关键设计:DNS、数据库、微服务均在 Cell 边界内
Global Transaction Router(GTR)作为统一入口 ✅ 已验证 GTR 强制所有跨 Cell 流量经其转发,禁止 Cell 直连
静态数据复制到 Cell,动态数据走确定性路由 ✅ 已验证 AmEx 数据本地性原则
不可回头点 + 唯一交易 ID + 幂等设计 ✅ 已验证 用于安全重试与重新路由
Cell 内观测本地优先、异步聚合到全局 ✅ 已验证 避免中心化 Observability 平台成为同步依赖
适用于所有业务一上来就照搬 ⚠️ 不建议 适合高价值、高可用、高规模、强 SLA 系统;流量小、模型未稳定时强上属于过度设计

很多系统谈高可用,最后都会落到几个熟悉的词上:限流、熔断、降级、重试、监控、告警、自动扩容、多活、异地容灾。

这些当然重要,但它们更像是"故障发生之后怎么办"。AmEx 在《Cell-Based Architecture for Resilient Payment Systems》里讨论的重点更靠前一层:系统一开始就要被设计成故障不会无限扩散的结构。

这也是 Cell-based Architecture 的核心价值。

在支付系统里,这个问题尤其关键。一次支付请求不是普通的读写接口,它可能同时牵涉商户、收单方、发卡方、风控、清结算、币种、国家市场、支付类型、协议格式和外部网络。支付链路对可用性、低延迟、确定性响应、数据一致性和重复交易控制都有很高要求。

只靠"服务挂了自动重启""接口超时就重试""数据库主从自动切换",并不能解决最危险的问题。真正危险的不是某一个服务坏掉,而是一个局部故障把整个支付网络拖下水。

Cell 架构要解决的,就是让系统"坏得有边界"。

1. Cell 不是一个服务,而是一套完整的小系统

最简单的理解是:一个 cell 是一套可以独立完成某类业务处理的系统单元。

它不是一个微服务实例,不是一个 Kubernetes namespace,也不是一组部署标签。一个成熟的 cell 通常包含一组微服务、数据库、缓存、消息组件、配置、观测组件,以及完成业务处理所需要的本地依赖。

也就是说,cell 是一个完整的小系统。

传统架构经常是这样的:入口网关后面是一组全局共享服务,服务之间互相调用,数据库集中部署,缓存集中部署,配置中心集中部署,日志链路集中部署。看起来资源利用率高,组件复用充分,但问题也很明显:任何一个共享组件出问题,都可能影响整个平台。

Cell 架构会把系统切成多个相对独立的处理单元。请求进入系统后,先由路由层判断应该进入哪个 cell。进入 cell 之后,尽量只访问本 cell 内的服务和数据。如果某个 cell 故障,理论上只影响被路由到这个 cell 的那部分流量,其他 cell 继续工作。

所以,cell 架构的第一原则不是复用,而是隔离。

2. Cell 架构和微服务不是同一件事

很多人会把 cell 架构理解成"更细的微服务拆分",这个理解不准确。

微服务解决的是业务职责拆分。订单服务、支付服务、用户服务、风控服务、账务服务,每个服务负责不同能力。

Cell 架构解决的是失败域隔离。它关心的是:一组服务、数据和依赖能不能形成一个独立运行边界。

所以,一个系统可以是微服务架构,但不是 cell 架构。比如你有 50 个微服务,但所有服务共享同一套数据库、缓存、配置中心、消息集群和下游依赖。一旦共享数据库故障,全系统仍然不可用。

反过来,一个 cell 里也可以包含多个微服务。AmEx 的支付处理平台就是微服务体系,但它把一组微服务、数据库和支撑组件组织成独立 cell。每个 cell 都能独立处理支付请求,并且在交易关键路径上没有同步跨 cell 依赖。

可以这样理解:

微服务是把业务拆开。Cell 是把失败关住。

3. AmEx 的关键设计:每个核心支付生态实例都是一个 Cell

AmEx 在文章里提到,他们从 2018 年开始现代化核心支付生态。这个平台处理实时卡支付交易,是面向持卡人和合作伙伴的关键系统。

在新平台里,每个核心支付生态实例都被设计成一个 cell。它具备几个特征:

  1. 可以独立部署。
  2. 拥有自己的微服务、数据库和支撑组件。
  3. 是一个明确的失败域。
  4. 可以从流量轮转中摘除,用于维护或故障处理。
  5. 在交易处理关键路径上没有同步跨 cell 依赖。

最后一点最重要:没有同步跨 cell 依赖。

很多系统表面上拆了多个区域、多个集群、多个 Kubernetes 环境,但服务之间仍然跨区域同步调用,或者业务处理时要去中心库查数据。这种架构看起来"多活",实际上还是强耦合。一个远端依赖慢了,本地 cell 也会慢;一个共享数据源坏了,所有 cell 都会坏。

AmEx 的思路是:交易进入 cell 后,处理过程必须尽量本地完成。DNS、数据库、微服务和支撑服务都在 cell 边界内。跨 cell 交互被严格限制,cell 不能退化成一个部署分组,而要成为真正的故障边界。

4. 数据本地性:支付系统不能每次都去中心系统查

支付处理离不开数据,比如币种汇率、商户类别码、合作方规则、市场配置、支付类型规则、交易状态等。

这里的难点是,不同数据不应该用同一种方式处理。

第一类是静态或半静态数据,例如币种汇率、商户类别码、市场配置。它们变化频率不高,可以提前复制到每个 cell。这样交易处理时就不需要临时访问中心系统,也不会因为 cache miss 去远端查询。

这背后的原则是:复制可以慢一点,但交易不能等。

复制发生在交易路径之外。交易处理不等待复制完成,也不依赖中心系统实时返回。这样既降低延迟,也减少故障传播路径。

第二类是动态数据,例如会随交易变化的状态。这类数据不能简单靠广播复制解决,因为复制延迟可能导致某个 cell 拿不到最新状态。

AmEx 的做法不是让所有 cell 都做全局强一致,而是用确定性路由把交易送到拥有正确数据的 cell。

这点很重要。很多系统一遇到数据一致性问题,就倾向于做全局同步、分布式事务、跨 region 强一致、集中状态服务。但这些方案如果放进支付关键路径,延迟和可用性都会受到影响。

更稳的做法是:如果最新状态只在某个 cell 权威可用,那路由层就应该知道如何把请求送到那个 cell,而不是让任意 cell 接到请求后再跨 cell 找数据。

不要在业务处理过程中补救路由错误,要在入口处就把请求送对地方。

5. GTR:不是普通网关,而是支付网络的交通控制层

AmEx 架构里有一个核心组件:Global Transaction Router,简称 GTR。

它不是普通意义上的 API Gateway。

普通网关通常负责鉴权、限流、转发、协议转换和日志。GTR 更像支付网络里的交通控制层。它要理解足够少、但足够关键的支付协议字段,从而做路由、故障转移、灰度、流量切换和 cell 边界控制。

这里有一个重要分寸:GTR 需要理解足够做路由的支付信息,但不能承载复杂支付业务逻辑。

如果 router 太薄,它无法判断请求应该进入哪个 cell。如果 router 太厚,它又会变成新的中心化业务系统,成为更大的故障点和复杂性中心。

所以 GTR 的定位可以概括为:懂协议,不做业务。

它知道如何基于合作方、市场、支付类型、交易属性等信息做确定性路由。但真正的支付处理逻辑仍然在 cell 内部完成。

GTR 还有一个更关键的作用:强制所有跨 cell 流量经过它。

如果一个 cell 不能处理某笔交易,需要重新路由到另一个 cell,也必须回到 GTR。cell 之间不能直接互相调用。

这条规则看起来麻烦,但它避免了一个非常常见的架构腐化过程:一开始大家都说 cell 之间不要互调,后来某个需求为了方便开了一个例外,再后来另一个服务也跨 cell 查一下,最后跨 cell 调用越来越多,cell 边界名存实亡。

AmEx 通过 GTR 把边界制度化。只有 GTR 能跨 cell 通信,普通微服务不能绕过它。

这才是架构约束,而不是团队约定。

6. 故障转移:不是恢复半截交易,而是安全重启

支付系统里的故障转移有一个难题:如果一笔交易在 cell A 里处理到一半,cell A 出问题了,能不能在 cell B 接着处理?

直觉上看,"接着处理"似乎更高效。但 AmEx 的做法不是跨 cell 恢复部分处理状态,而是在安全边界内用原始交易数据在另一个健康 cell 重新开始处理。

原因很清楚:跨 cell 恢复半截状态,会引入共享状态、状态同步、部分提交、幂等冲突、重复请求、外部系统副作用等一系列问题。为了恢复一笔交易,可能会破坏整个 cell 隔离模型。

所以更合理的是 restart model:

如果交易还没有到达不可回头点,失败后可以重新路由到另一个 cell,从原始交易数据重新处理。

如果交易已经发送到外部系统,例如发卡方,那就进入不可回头点,不能再随意重新路由。因为外部系统可能已经收到请求,重复发送可能造成重复授权或状态不一致。

这对后端系统很有启发。

真正可靠的系统不是到处保存中间状态,然后试图精确恢复每一步;而是把流程设计成:

在关键副作用发生前,可以安全重试、重新路由、重新执行。

在关键副作用发生后,必须依赖幂等键、交易唯一标识、状态机和补偿机制控制重复影响。

AmEx 对其他支付类型也使用唯一交易标识来管理幂等。交易在重试和重新路由中保持同一个唯一 ID,下游系统用这个 ID 识别和抑制重复请求。

所以 cell 架构不是单独成立的。它必须和幂等设计、状态机设计、外部副作用边界一起工作。

7. 越靠近入口,依赖越要少

AmEx 文章里还有一个很实际的点:越靠近入口,依赖越要少。

GTR 是支付网络入口,一旦它阻塞或不可用,会直接影响交易进入系统。所以它不能依赖太多外围系统,尤其不能让日志、配置、监控这类非关键能力阻塞交易处理。

比如日志系统不可用时,交易不能因为写日志失败而失败。文章中提到的做法是使用异步日志和缓冲区截断策略。如果缓冲区满了,就丢日志,而不是阻塞支付请求。

这在工程上很现实:关键路径上,宁可丢非关键观测数据,也不能阻塞核心交易。

配置也是类似。配置服务不可用时,系统应该继续使用最后一次已知配置运行,而不是立刻停摆。配置异步更新,关键路径读取本地内存里的配置快照。

这类设计背后的共同原则是:关键路径只依赖必须依赖的东西。

日志、指标、配置、控制面、管理面、全局看板,都不应该成为交易处理的同步前置条件。

很多系统的可用性问题并不是核心业务代码坏了,而是被旁路依赖拖死。比如日志打不出去导致线程阻塞,配置中心抖动导致服务拒绝请求,监控 SDK 卡住导致接口延迟飙升。这些问题在普通业务里已经危险,在支付入口层更不可接受。

8. 观测也要本地优先

很多人设计 cell 架构时会关注业务服务和数据库隔离,但忽略观测系统。

AmEx 的做法是:每个 cell 先把日志、指标、trace 发布到 cell 本地的观测组件,然后再异步聚合到全局平台,用于统一看板、告警和分析。

这样做的好处是,如果某部分观测系统出问题,只会降低对应 cell 的可见性,而不是影响整个平台的交易处理,也不会影响所有 cell 的观测采集。

可以抽象成一句话:

观测要全局可见,但采集不能全局强依赖。

也就是说,全局看板可以异步聚合,但 cell 内部必须先能独立采集、独立判断、独立运行。不要让中心化 observability 平台成为所有 cell 的同步依赖。

9. Cell 架构的收益不只是容灾

Cell 架构最直观的收益是降低故障爆炸半径。

如果一个系统有 10 个 cell,每个 cell 承担一部分流量,那么单个 cell 故障时,理论影响范围会被限制在对应流量或对应分区内。其他 cell 继续处理请求。

但它还有两个额外收益。

第一是低延迟。交易进入 cell 后,服务调用、数据库访问、支撑依赖都尽量本地完成,减少跨区域、跨集群、跨共享服务的网络跳转。外部依赖越少,延迟越可预测。

第二是扩展性。传统系统扩展经常是把一个大系统变得更大:数据库更大、缓存更大、集群更大、消息队列更大。问题是系统越大,隐藏瓶颈越多,压测越难,故障影响面越大。

Cell 架构更倾向于复制多个有上限的小系统。每个 cell 有明确容量上限,压测可以围绕单 cell 进行。超过容量后,不是无限扩一个 cell,而是增加新的 cell。

这是一种从 scale up 到 scale out 的思路变化。

更重要的是,单 cell 的极限更容易测试。一个巨大共享系统很难完整压到边界,但一个固定规模 cell 可以被反复压测、故障注入、验证容量水位。系统增长时,复制经过验证的 cell,而不是不断放大未知复杂性。

10. 代价:资源、数据、路由和组织纪律

Cell 架构不是免费的。

第一个代价是资源和运维成本。每个 cell 都要有自己的服务、数据库和支撑组件。某些共享服务在传统架构里只要一套,在 cell 架构里可能要复制多套。

第二个代价是数据设计更复杂。哪些数据可以提前复制?哪些数据必须路由到权威 cell?哪些数据可以异步最终一致?哪些数据不能跨 cell 共享?这些都要非常清楚。

第三个代价是路由层复杂度上升。router 必须知道用什么 partition key 或交易属性做路由。这个 key 必须稳定、可获得、可解释,不能在关键路径上依赖复杂查询。

第四个代价是组织纪律要求更高。cell 边界很容易被临时需求腐蚀。只要跨 cell 同步调用一多,cell 架构就会退化成普通分布式系统,而且复杂度更高。

第五个代价是发布和运维策略要重做。不能所有 cell 同时发布,否则坏版本会同时影响全局。更合理的方式是按 cell 分批发布、灰度、回滚、观察。

所以 cell 架构适合高价值、高可用、高规模、强 SLA 的系统,不适合所有业务一上来就照搬。

对于普通后台系统,如果流量不大、可用性要求不极端、数据模型还没稳定,强行上 cell 架构只会过度设计。

11. 普通业务系统怎么借鉴?

不要一开始就问"我要不要上 cell 架构"。

更好的问题是:我的系统里有没有必须被隔离的失败域?

一个租户故障,能不能不影响其他租户?

一个地区故障,能不能不影响其他地区?

一个客户的异常请求,能不能不拖垮全站?

一个坏版本发布,能不能只影响一小部分流量?

一个数据库分片故障,能不能只影响对应业务分区?

如果这些问题的答案很重要,才值得考虑 cell 化。

一个可落地的演进路径通常是:

  1. 明确 partition key。可以是租户 ID、客户 ID、商户 ID、地区、业务线、支付类型、订单归属等。这个 key 必须在入口请求阶段就能确定。
  2. 把路由层做薄。router 只负责路由、灰度、故障切换和流量控制,不承载复杂业务逻辑。
  3. 拆出本地数据边界。能提前复制的数据提前复制,必须强一致的数据路由到权威 cell,不能让业务服务在处理过程中到处跨 cell 查数据。
  4. 禁止 cell 间随意互调。跨 cell 通信必须走统一控制点,否则边界会失效。
  5. 设计幂等和重启模型。请求失败后是重试、重放、补偿,还是终止,必须由状态机和副作用边界定义清楚。
  6. 把发布、观测、容量和故障演练按 cell 组织。cell 不是部署标签,而是运维单位、容量单位、发布单位和失败单位。

12. 对 Java 后端和平台工程的启发

对于 Java 后端系统,cell 架构不是某个框架能直接提供的能力。Spring Cloud、Nacos、Gateway、Dubbo、Kubernetes、Istio、Envoy 都只是工具。真正重要的是系统边界设计。

如果落到工程实现,可以对应到这些方面:

网关层需要支持基于业务 key 的确定性路由,而不只是简单负载均衡。

注册发现需要区分 cell 内服务和跨 cell 入口,避免服务发现直接暴露所有实例。

数据库要按 cell 划分连接和权限,服务只能访问本 cell 数据库。

缓存、消息队列、配置、限流、熔断策略也要按 cell 维度设计,不能所有 cell 强依赖同一套中心组件。

发布系统要支持 cell-by-cell 灰度,而不是全量滚动。

监控告警要同时有 cell 内视角和全局视角。

压测要先压单 cell 的容量边界,再推导整体容量模型。

故障演练要验证一个 cell 故障时,流量是否能摘除、是否会跨 cell 传播、是否会产生重复处理。

这比"加几个熔断注解"复杂得多,但也更接近真正的高可用工程。

13. 最核心的结论

AmEx 这篇文章最值得记住的不是某个组件名字,而是几个架构判断:

高可用不是让所有组件都不失败,而是让失败被限制在可控边界内。

Cell 不是部署单元,而是完整业务处理单元和失败域。

关键路径上不能有同步跨 cell 依赖。

静态数据复制到 cell,动态强一致数据通过确定性路由找到权威 cell。

Router 要足够聪明以完成路由,但不能变成业务大脑。

故障转移不是恢复半截状态,而是在安全边界内基于原始请求重新开始。

幂等、唯一交易 ID、不可回头点,是支付系统能安全重试和重新路由的前提。

越靠近入口,依赖越少;非关键能力必须让位于核心交易处理。

Cell 架构的本质不是多部署几套系统,而是用架构纪律控制复杂系统的失败传播。

真正成熟的支付系统,不是没有故障,而是故障发生时,系统知道哪里可以坏、坏到什么范围、如何摘除、如何重试、如何恢复,以及哪些边界绝对不能被突破。

这就是 AmEx 这篇 cell-based architecture 实战最有参考价值的地方。


参考资料


错误速查卡

症状 根因 定位 修复
单个 cell 故障导致全平台不可用 "多活" 形似神不似,服务仍有同步跨区域/跨 cell 调用,或共享中心数据库 追踪服务调用链与数据连接:是否存在跨 cell 同步 RPC、中心库查询 严格遵守 cell 边界,关键路径上消除同步跨 cell 依赖;动态强一致数据改走 GTR 确定性路由
关键路径上日志/配置/监控抖动直接拖垮交易 关键路径同步依赖了非关键旁路能力(日志 SDK、配置中心、监控采集) 故障时刻的调用栈与依赖健康度,看是否存在写日志/读配置/打点的同步阻塞点 关键路径上日志改异步+截断、配置改本地快照、监控改本地优先异步聚合
故障转移后出现重复扣款/状态不一致 跨 cell 直接恢复半截状态,或重新路由时未保持唯一交易 ID 核对失败交易在外部发卡方侧的请求记录与 cell 内状态机日志,看是否多次触达不可回头点 改为 restart model:不可回头点前重路由重启,不可回头点后依赖幂等键 + 唯一交易 ID
cell 架构运行一段时间后边界失效 临时需求开了跨 cell 直连的"小口子",跨 cell 同步调用逐步增多 静态扫描服务间调用关系、看是否绕过 GTR 直连 强制所有跨 cell 通信经 GTR,把边界做成架构约束而不是团队约定
普通业务系统强上 cell 架构后运维/资源成本爆炸 流量规模与 SLA 与 cell 模式不匹配,数据模型也未稳定 评估 partition key 是否清晰、是否存在权威 cell、流量是否足够分摊多 cell 成本 不直接上 cell,先问"是否存在必须隔离的失败域",按 6 步演进路径逐步推进
router 越来越重、变更越来越难 router 越权承载业务逻辑,从路由层退化为中心化业务系统 看 router 内部是否出现业务规则编排、复杂计算、依赖业务库等代码 把 router 拉回"懂协议、不做业务"的定位,业务逻辑下沉到 cell 内部

作者:武子康的个人博客

相关推荐
米小虾1 小时前
Prompt Engineering —— 意图的精确表达
人工智能·agent
IT_陈寒2 小时前
React状态更新总是慢半拍?你可能忘了这个默认行为
前端·人工智能·后端
aneasystone本尊2 小时前
学习 turbovec 的混合检索与框架集成
人工智能
火山引擎开发者社区13 小时前
火山AgentPlan/CodingPlan同步上线GLM-5.2
人工智能
冬奇Lab13 小时前
Skill 系列(05):Skill 工作流串联——4 种模式实测,并发加速 1.5x
人工智能·开源
冬奇Lab14 小时前
每日一个开源项目(第141篇):hiring-agent - HackerRank 开源了他们的简历评分系统,你的简历能得几分?
人工智能·面试·开源
甲维斯14 小时前
又升级咯!坦克大战2026,科技与复古并存!
前端·人工智能·游戏开发