无服务器场景(serverless)的容错怎么做?我们的设计

无服务器架构和FaaS(Function-as-a-service 函数即服务)近年来越来越受欢迎,这要归功于易用性、自动伸缩和按需付费的特性。然而,FaaS 基础设施中缺少应用程序的容错能力。

把坑留给码农?

默认情况下,AWS Lambda 或 Google Cloud Functions 等 FaaS 系统需要开发者自己考虑执行失败的情况。云服务的策略是:无论是应用程序自身错误还是基础设施故障,一旦函数执行失败就执行重试。这意味着函数可能会运行多次。

并且更可怕还有,你的业务代码也可能会运行 0.5 次,或者 3.2 次……这是怎么回事?

这个糟糕问题的原因是,大多数 FaaS 系统提供的能力不能保证资源层(如数据库或文件等)在执行失败后被清理。

在失败和重试的过程中,修改共享状态的应用程序会在不知不觉修改部分结果。如果一个请求将更新两个键 k 和 l,但函数在两次更新之间崩溃,那么现在客户端将看到较新版本的 k 和较旧版本的 l。

这就是云平台 FaaS 的现状。

FaaS 的无痛容错:原子性

为了避免这种类型的异常,开发人员需要一个简单的保证,那就是原子性:要么一个请求的所有操作全部成功,要么全部失败。传统做法中,原子性是由强一致性(事务式)存储引擎来保证的,但这些系统有众所周知的扩展和性能问题。如何才能让 FaaS 执行保证原子性?

为此,我们构建了一个名为 AFT(Atomicity for Fault Tolerance)的系统,它是一个位于任何无服务器计算层(如AWS Lambda、Google Cloud Functions)和存储层(如AWS DynamoDB、Redis)之间的胶合层。计算层的每个逻辑请求(可能由多个函数组成)都被视为一个事务。AFT 保证事务所做的所有更新都会在存储层原子化执行。

AFT 的设计是灵活的。我们对计算层不做任何假设,而对存储层的要求就是存储层要持久化。我们可以保证在 DynamoDB 和 S3 等最终一致的系统上运行的函数的原子性。AFT 有两个主要特点。(1)无协调、原子更新;(2)保证事务只读取已提交的数据。AFT 将每个新的key版本写到不同的物理存储位置,以避免写-写冲突。

为了保证事务读取语义上一致的数据,AFT 保证了读取原子性。读取原子性要求客户机只从已提交的事务中读取数据,并按照事务提交的顺序进行读取。也就是说,如果事务 T1 写了 key 的版本 K1,而后来的事务 T2 写了 K2 和 L2,那么客户端就读不到 K1 和 L2,因为事务 T2 写了一个较新版本的K,称为 K2。所有这些都可以在没有任何协调¹的情况下完成。这种类型的异常被称为断裂读。

下一步

我们对胶合层架构作为探索不同开发者保障的手段感到兴奋。特别是有一整类应用可以从无服务器架构中受益,但需要找到将更强的一致性引入 Faas 世界的方法。

熟悉数据库内部的朋友可能已经注意到,我们这里讲到的读取原子性保证类似于无协调版本的快照隔离,这是传统数据库中常用的强一致性形式。我们正计划在此背景下探索如何将强一致性引入到无服务器应用中。如果你对这些有兴趣可以通过原文链接反馈给我们。

原文地址:

https://medium.com/riselab/solving-serverless-computings-fault-tolerance-problem-122128fa5787

完整论文链接:

[1] https://arxiv.org/pdf/2003.06007.pdf

本文由高可用架构翻译。技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。

高可用架构

改变互联网的构建方式