用于弹性支付系统的单元化架构

Hacker News Top 新闻

摘要

美国运通描述了其核心支付生态系统采用的单元化架构,该架构能够隔离故障、降低延迟并扩展容量。这种方法将微服务和数据库分组到独立的单元中,以限制爆炸半径。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/18 23:52

# 基于单元架构的高韧性支付系统 来源:https://americanexpress.io/cell-based-architecture-for-resilient-payment-systems/ 美国运通核心支付生态系统是一个全球平台,服务于世界各地的持卡会员和合作伙伴。每天,它处理着需要高可用性、低延迟和可预测性能的实时支付交易。 韧性并非事后才考虑的因素,它从一开始就被编入了系统设计。局部故障被限制在既定边界内,恢复机制设计得快速且可预测。 为了实现这一点,该平台构建在一种基于单元(cell-based)的架构之上,这种架构能够隔离故障、保持低延迟处理,并在不扩大故障域的情况下扩展容量。 这篇博客概述了指导这一架构的原则,以及它们如何帮助我们构建一个全球规模的高韧性支付平台。 ## 核心支付生态系统 2018年,我们启动了一项现代化改造核心支付生态系统的旅程。这个平台处理实时的卡片和支付交易,对我们的持卡会员和合作伙伴而言至关重要。 在平台现代化过程中,韧性始终是首要的设计要求。我们需要一种架构,即使单个组件发生故障,也能继续可靠地处理交易。这一决策很大程度上受到了我们历史设计模式的影响,这些模式虽然在“单元架构”这个术语出现之前就已存在,但共享了许多相同的原则。 我们的新平台瞄准云原生技术,这意味着我们需要以不同的方式思考如何为韧性和可扩展性进行设计。 在接下来的章节中,我们将讨论核心支付生态系统遵循的一些设计原则,以及它们如何不仅提高了我们可靠处理支付的能力,还有助于降低延迟和更轻松地扩展。 ## 什么是单元架构? 单元架构是一种在云原生分布式系统领域日益流行的架构模式。 其核心思想是将相关的微服务、数据库和其他组件分组到称为“单元”(cell)的独立实例中。每个单元能够独立运行,不依赖其他单元。 基于单元架构的概念图 *在此图中:每个单元包含自己的服务和数据,因此故障会局限在该单元内,而不会扩散到整个平台。* 单元架构的主要优势是缩小故障的"爆炸半径"。由于每个单元都是独立的,如果一个单元出现问题,不会影响其他单元。其代价是单元架构通常会增加管理开销和架构复杂性,因为它需要精心设计以确保单元真正独立,并且数据得到适当本地化。 然而,对于像支付这样的关键任务系统,我们认为缩小爆炸半径和提高韧性的好处超过了额外的复杂性。 我们还发现,如果实施得当,单元架构可以通过减少外部依赖和网络跳数来帮助平台降低延迟,并通过引入额外的独立单元来改善扩展能力。 ## 我们如何遵循单元架构 我们核心支付生态系统的每个实例都被设计成一个单元,它: - 是一个可独立部署的单元,能够自行处理支付。 - 拥有自己的一套微服务、数据库和其他组件。 - 是一个单一的故障域,意味着如果一个单元出现问题,故障不会级联到单元边界之外。 - 可以为了维护或应对故障而退出轮换,而不会影响整个系统或需要与其他单元协调。 - 在处理事务的关键路径上没有同步的跨单元依赖。 一个单元由其故障边界定义,而不是由特定的基础设施构造定义。在实践中,单元从不跨多个区域——处理事务所需的一切(DNS、数据库、微服务和支撑服务)都保持在该边界内的本地。 为了实现这一点,我们遵循一套核心原则来指导我们的设计决策,并帮助我们确保单元真正独立且具有韧性。 ## 默认的数据和处理局部性 处理支付需要数据:汇率、商户类别代码等等。有些数据是静态的,有些数据则随每笔交易而变化。 ### 静态及半静态数据复制 对于静态或半静态数据,如汇率和商户类别代码,我们将这些数据复制到每个单元。 静态和半静态数据复制 *在此图中:参考数据提前推送到每个单元,这样事务处理就永远不需要对中央源进行同步查找。* 我们不是在事务处理过程中依赖于对中央记录系统的穿透式读取,而是提前在每个单元中预填充这些数据。这使得参考数据在事务到达之前就保持本地可用,避免了处理过程中的缓存未命中延迟,并保护了关键路径的隔离性。 复制工作发生在事务路径之外,这使我们能够在本地保持数据可用,而无需引入同步的跨单元依赖。 ## 动态数据路由 并非所有数据都是静态的,也并非所有数据都能预先填充。对于更动态的数据(随每笔事务变化的数据),数据复制可能不够快,无法确保每个单元在正确的时间拥有正确的数据。我们不想将事务路由到没有最新数据的单元,因为那会增加延迟并可能导致处理失败。 相反,我们使用确定性路由将事务路由到已有正确数据的单元。在最近的一篇文章《零停机迁移支付网络两次》(https://www.americanexpress.io/migrating-the-payments-network-twice/)中,我们介绍了全局事务路由器(Global Transaction Router),它负责管理连接并将事务路由到适当的单元。它能够做到这一点,是因为它了解足够多的支付规范,从而能够根据事务数据做出路由决策。 例如,我们可以根据合作伙伴、市场或支付类型进行路由;如何路由取决于支付事务数据和用例,但关键在于,当跨事务需要强数据一致性时,我们有选择地将事务路由到需要的地方。 动态数据路由与复制 *在此图中:路由器将事务发送到已经拥有权威动态状态的单元,同时复制操作在关键路径之外异步进行。* 我们通过将微服务通信限制在单元Kubernetes网络内的Pod到Pod交互,来保持事务处理的本地化,确保所有处理都在单元边界内进行。 为了确保使用基于消息的复制在单元之间同步故障切换数据,该复制在事务路径之外异步进行,因此不会影响延迟或可用性。 没有进行中的事务会等待复制完成;如果需要最新状态,全局事务路由器会将事务发送到该数据已经是权威或可用的单元。 我们只允许微服务与本地化的数据库实例通信。这保持了延迟的可预测性,并避免了不必要的网络跳数,但这需要经过深思熟虑的路由决策。 通过在边缘引入确定性路由,我们可以确保事务被路由到已有正确数据的单元。 ## 强制的入站和出站边界 除了路由能力,全局事务路由器还是我们“仅限本地”处理的关键执行者。 事务必须通过全局事务路由器进入单元;如果一个单元无法处理某个事务,并且该事务需要被重新路由到另一个单元,也必须通过全局事务路由器。 这样,全局事务路由器也充当了一个支付网格,将我们的单元全局连接起来。 入站和出站边界强制 *在此图中:所有跨单元流量都通过全局事务路由器,从而保持严格的单元边界。* 随着平台的增长,防止跨单元依赖变得越来越困难。 通过全局事务路由器严格控制跨单元通信,我们防止了单元之间形成强依赖关系,因为它们完全无法自行通信——只有全局事务路由器才能跨单元通信。 这种强制有时会导致服务重复,而在某些情况下共享实现可能看起来更简单,但它维护了单元的独立性,并通过减少跨单元网络跳数改善了延迟。 同样的原则也适用于可观测性。每个单元首先将日志、指标和追踪发布到该单元内部的本地化可观测性组件,因此可观测性栈的部分丢失只会降低该单元的可见性,而不是整个平台。我们仍然异步聚合可观测性数据,以提供全局仪表板、告警和整个机群的分析,但这种聚合仍然在事务关键路径之外。 ## 单元独立故障;其他单元取而代之 利用将事务重路由到其他单元的能力是我们韧性战略的关键部分。 当故障发生时,其影响被限制在受影响的单元内,事务会自动被重路由到一个健康的单元,在那里重新开始处理。 我们不仅重路由新传入的事务,也重路由已经在故障单元中正在处理的事务。 我们的支付处理子系统遵循一种编排式微服务架构,其中一个编排器微服务管理工作流处理流程,并调用其他微服务执行特定任务。 如果下游服务开始失败,编排器会检测到失败,停止处理,并将事务发送回全局事务路由器,以便重路由到另一个单元。 故障时重路由 *在此图中:当一个单元在流程中途失败时,事务被重路由并在健康单元中重新开始,而不是跨单元恢复。* 我们不会尝试跨单元恢复部分处理的事务。相反,我们使用原始事务数据在另一个单元中重新启动事务处理。 只有当事务仍处于核心支付生态系统内部时,这种重启才是安全的。一旦事务被发送到外部系统(例如,发卡机构),我们认为那是一个不可返回点,在此之后不允许事务被重路由。 卡片授权的结构使得不可返回点更靠近处理流程的末尾。如果事务在到达不可返回点之前失败,我们可以安全地重路由并重新启动处理,而无需担心重复事务或数据一致性问题。 对于其他支付类型,我们通过事务标识符管理幂等性。每个事务携带一个唯一的事务标识符,该标识符在重试和重路由过程中保持不变。下游系统使用这些标识符来检测和抑制重复请求,从而允许安全地处理重试和重路由,而不会引入不一致或重复事务。 重启模型强调了避免单元间共享状态的重要性。跨单元共享状态会引入同步挑战和潜在的一致性问题,特别是在故障转移场景中。单元之间的通信故障可能影响全局处理事务的能力,对于支付系统来说,我们必须不惜一切代价避免这种情况。 在我们的架构中,单元被设计为松耦合的。每个单元都有自己的数据库集群,单元内的微服务只与本地数据库集群通信。 当一个单元发生故障时,其影响被限制在该单元内,允许其他单元继续正常处理事务。 当被重路由时,事务不需要依赖前一个单元的状态就可以进行处理。 在任何时刻,一个单元都可以退出轮换。当一个单元自动或手动退出轮换时,另一个单元会取而代之。这不一定是二进制的切换。正如《零停机迁移支付网络两次》(https://www.americanexpress.io/migrating-the-payments-network-twice/)中所讨论的,全局事务路由器可以按百分比在单元之间转移流量,从而使我们可以逐步排空一个单元进行维护,在部分负载下验证恢复中的单元,或者在事件期间更安全地做出响应。 ## 边缘的最小依赖 由于全局事务路由器位于边缘,它是一个提供连接、路由和韧性的关键服务。为了确保其可用性,我们力求将该系统内的依赖项保持在尽可能小的范围内。 越靠近边缘,我们追求依赖越少。 但我们不只是减少依赖;我们还力求将它们排除在关键路径之外。 如果我们的日志基础设施变得不可用,我们不希望这影响到处理事务的能力。为此,我们使用了一个配置有缓冲区截断策略的异步日志记录器,这样如果缓冲区满了,我们会丢弃日志而不是阻塞事务处理。 如果我们的配置服务不可用,我们希望继续使用上次已知的配置运行。为此,我们维护一个异步更新的内存配置,因此如果配置服务变得不可用,我们可以继续使用上次已知的配置,直到它再次可用并且我们可以拉取最新配置。 减少边缘的依赖 *在此图中:边缘路径通过异步处理日志和配置来保持精简和韧性,而不是让这些依赖阻塞事务。* 将依赖项排除在关键路径之外减少了故障点。这需要有意的权衡:接受非关键功能(日志记录、指标)的降级,以保护事务处理。 ## 总结 在分布式支付系统中,韧性不仅仅是靠监控和重试来实现的——而是通过定义清晰的故障边界并通过设计强制执行来实现的。 通过将我们的核心支付生态系统组织成隔离、可独立恢复的单元,我们将重大故障转化为可控的路由决策。局部性、确定性路由、幂等处理和严格的边界强制执行共同作用,确保增长和变更不会增加风险。 这种纪律构成了我们单元架构的基础,使我们能够以低延迟和高韧性运营一个全球支付平台——这些原则将继续塑造我们的演进。

相似文章