我们如何在Adyen做出架构决策 - Adyen


在 Adyen,我们有一种非常务实的方法来解决问题。因此,我们使用简单的工具来取得出色的成果。
本博客的目标是向您介绍我们在扩展系统时面临的挑战、我们如何应对这些挑战,以及我们的系统因这些决定而呈现的样子。特别是,我们将关注自主开发的解决方案与开源软件之间的选择。
在博客的第一部分,我们将讨论与我们的边缘服务相关的这些主题,在第二部分,我们将对我们的会计和报告层做同样的事情。
与其仅仅解释我们的架构看起来如何,我们认为解释为什么我们的架构看起来会很有趣。在 Adyen,我们坚信赋予开发人员责任。这意味着负责系统的设计和实施以及该系统的安全和维护。因为我们系统的设计是由同时值班的工程师完成的。这些工程师在决定如何构建某些东西时是强有力的贡献者,这有时会导致违反直觉的结果。
 
是自己构建本土解决方案还是选择开源软件?
Adyen 的新加入者经常对我们自己构建东西的某些情况感到惊讶。因此,我们认为在浏览我们的架构时,讨论其中一些选择会很有趣。有关我们自己建立银行的极端示例,请观看有关我们为何建立自己的银行的简短视频
当我们面临挑战时,建议的解决方案通常是引入新工具或框架,或者在内部编写一些东西。从 Adyen 的角度来看,自己编写一些东西会给你更多的灵活性,但与开源替代方案相比,它会花费更多的时间并且可能具有更少的功能。
由于更好的文档和更大的社区,开源选项的可用性可能更高,但它可能更复杂,因为我们不需要额外的功能,并投资培训需要使用它的人员。开源选项的安全性可能会更好,因为很多人都审查过它,但攻击面也几乎总是更大。
 
对供应商解决方案的看法
许多企业也会考虑供应商解决方案,我们也是。但是,我们希望专注于我们系统中的核心流程,对于这些流程,我们从不选择专有解决方案,因为我们希望拥有完全控制权。
我们购买而不是构建,如果用例是外围的,它不必是实时的,我们不必嵌入它,或者如果它是一个很好的价值主张。这方面的一个例子是我们的一些 KYC 背景调查员。当然,避免锁定是这里的一个重要考虑因素。
 
Adyen架构
Adyen 从事处理付款业务。我们收到商家的付款;我们将该付款发送给第三方(例如信用卡公司),然后返回结果。这种情况每秒发生数百次,24/7。我们还跟踪所有交易,以便我们可以在从银行收到钱后将钱转给商家。当然,我们也会向我们的商家提供报告。
我们每年为此花费数千亿欧元。在过去的几年里,我们推出了其他产品,例如发卡银行Adyen for Platforms,这有助于平台业务,如拼车或市场。我们在一个平台上完成所有这些工作,在一个存储库 (monorepo) 中,几乎完全用 Java 编写。
我们的系统分为几个部分,它们的功能相对独立。它们沿着业务领域划分。例如,我们有一部分以支付处理为中心,另一部分以银行为中心。在数据层,它们被捆绑在一起。相同的设计原则适用于每个子系统。因此,虽然我们在本博客中主要介绍了支付处理,但架构总体上是相似的。
商家发送的付款将到达我们的边缘层。这里我们会做同步处理,如果需要联系支付方式,再将结果返回给商家。可用性和低延迟在这里至关重要。同时,我们将此付款发送到我们的后端系统,我们将其存储在我们的会计系统中。准确性和可靠性是这部分系统的关键优先事项。最后,支付在我们的数据处理层结束,吞吐量成为一个主要问题。我们将逐一介绍这些层,讨论我们为塑造它们所做的选择。
 
边缘服务
对我们系统的每个 API 调用都首先通过我们的边缘服务。付款可以来自支付终端移动应用程序、直接 API 调用或我们托管支付页面。支付接受层 (PAL) 是我们边缘层中的一项关键服务。所有付款都通过它。
此应用程序会将付款发送到我们的其他内部服务。这些其他服务可以是风险引擎、用于保存或生成循环支付令牌的服务,或用于计算哪个连接将导致最高授权率的服务。它还将联系(通过中介服务)处理付款的合作伙伴付款方式或方案。
一个重要的设计特点是所有支付都在 PAL 处被抽象化,因此系统可以将它们视为平等。当然,它们之间是有区别的。有些会有额外的元数据(对于销售点交易,这可能是处理它们的终端的 ID)。但是,它们都通过相同的系统并存储在相同的数据库中。
处理初始设计的工程师已经在以前的支付公司获得了经验。在该公司中,进入系统的付款会在边缘层保留一些状态。如果新的付款会修改原始付款,例如退款,则可以立即处理,因为所有必需的信息都已存储在边缘层中。
这种设置的问题是双重的。应用程序不能停机维护或崩溃而不影响我们处理事务的能力。另一个问题是新机器无法立即处理与旧应用程序相同的卷。某些事务需要转到特定实例。应用程序中的状态使每个实例都是唯一的。
 
无状态服务
退后一步,可以看出为什么我们在 Adyen 采取了不同的做法。系统这部分的优先级是高可用和低延迟。我们应该始终能够接受付款并尽快处理它们。我们决定异步处理修改,而不是将状态保留在我们的边缘层中,从而使边缘层保持无状态。
因此,任何 PAL 实例都可以在不影响我们处理付款的能力的情况下关闭,并且新的 PAL 可以立即与其他已经运行的 PAL 一样有效。这使我们的水平缩放线性。换句话说,如果一个 PAL 每秒可以处理 X 笔付款,那么两个 PAL 每秒可以处理 2X 笔付款。该机制自公司成立以来基本未变,可见其威力。
边缘服务是无状态的,这意味着它们不能直接写入集中式数据库。这不仅有利于扩展系统,而且是一个非常好的安全功能。我们所有暴露在外部的系统都被禁止写入中央数据库,从而降低攻击者破坏或窃取有价值数据的风险。通过在开发人员中根深蒂固的安全意识,我们可以通过设计获得安全性,而不必回溯性地修补系统中的漏洞。
 
分布式数据库
最近,我们面临着使我们的支付 API 具有幂等性的挑战。这意味着如果商家向我们发送两次完全相同的付款,我们应该只处理一次,但在两种情况下都返回相同的响应。
正如您现在所知,我们不想通过将某些商家的付款限制在某些机器上来实现这一目标,因为这意味着这些机器不再具有线性可扩展性。这些信息需要在本地可用,因此我们最终决定将分布式数据库Cockroach与我们的 PAL集成。
我们可以在这里自己构建一些东西(可能在 PostgreSQL 之上),但这实际上不是我们的核心业务,并且有几个开源选项满足我们的标准。然而,正确了解数据库并将其优化到我们对它感到满意的程度需要付出大量的努力。有关在开源和自行构建之间做出决定的另一个示例,请参阅有关我们的图风险引擎的博客
 
未来的迭代
我们边缘服务的下一个重要步骤是动态扩展它们。我们在全球多个数据中心管理自己的基础设施,并提供裸机,但硬件和软件仍然紧密耦合。
我们不部署到云的原因部分是历史原因,部分是法律原因,部分是技术原因。在某种程度上,因为我们现在需要动态扩展,我们正朝着在内部云上运行我们的系统迈进。关于容器化工作的博客即将发布。

作者:Adyen 技术主管 Willem Pino