Meta无服务器平台每天处理数万亿次函数调用


Meta 的Meta的XFaaSXFaaS是无服务器平台,每天在分布于数十个数据中心区域的 100,000 多台服务器上处理数万亿次函数调用。

XFaaS 是 Meta 的超大规模和低成本无服务器函数的私有平台。XFaaS 是 Meta 的公共函数即服务 (FaaS) 选项的内部版本,它比 AWS Lambda、Azure Functions 和 Google Cloud Functions 更高效。

有趣的数据和启示:
这篇论文的一个关键点是,硬件的使用可以通过软件来优化,以提高无服务器性能。

  • Meta 认识到了无服务器函数启动开销的浪费,旨在模拟通用 Worker,即任何 Worker 都可以立即执行任何函数,而无需启动开销。
  • 大规模硬件成本巨大,每一个百分比都很重要。
  • XFaaS 仅用于非面向用户的函数。无服务器函数有太多的可变延迟,无法持续用于面向用户的函数。
  • XFaaS 的客户端以高度尖峰的方式提交函数调用。峰值需求是非峰值需求的 4.3 倍:

他们展示的一个负载示例是,在 15 分钟内向 XFaaS 提交了 2000 万次函数调用。
Meta 发现,即使是尖峰函数也有规律可循,他们利用这些规律使尖峰函数在工作负载上更具可预测性。

XFaaS 的效率如何?
XFaaS 的 CPU 日平均利用率为 66%,远高于行业平均水平。

  • 它利用时间(通过延迟函数)和空间(通过将其发送到负载较轻的数据中心)有效地分散负载。
  • Meta 正在继续将更多的函数过渡到非高峰时段,因为非高峰时段的负载和成本更可预测。
  • 由于是内部云,Meta 能够执行许多独特的优化,例如在同一进程中运行来自不同用户的多个函数。
  • 大多数函数的执行时间不到一秒,但并非所有函数都是如此。

XFaaS 解决的问题
问题:冷启动时间过长。

  • 如果容器过早关闭,下一次调用时必须重新初始化整个容器。
  • 如果容器关闭过晚,它就会闲置,浪费宝贵的计算资源。
  • 解决方案XFaaS 采用及时编译等方法,近似保证每个工作者都能立即执行任何函数。

问题:负载差异大。

  • 过量配置会导致额外的硬件成本,或配置不足时系统运行速度变慢
  • 解决方案XFaaS 将运行容错函数的时间推迟到非高峰时段,并在全球范围内跨数据中心区域分配函数调用。

问题:下游服务超载。

  • 举例说明:有一次,非面向用户的函数调用激增,导致面向用户的在线服务中断。
  • 解决方案XFaaS 采用类似于 TCP 拥塞控制的机制来调节函数的执行。

与公共 FaaS(AWS Lambda、Google 云函数、Azure 函数)的比较

  • 公共云 FaaS 将函数执行限制在单个数据中心区域内,而 XFaaS 可以在全球范围内调度函数调用,从而实现更好的负载平衡。
  • FaaS 平台首先关注减少延迟,忽略硬件利用率。XFaaS 注重硬件利用率和函数调用的吞吐量。

公共 FaaS 的经验教训
可使公共云受益的 XFaaS 技术包括

  • 允许调用者指定函数执行的开始时间。
  • 允许函数所有者设置有关完成期限的服务级别目标(SLO)。(低 SLO 可以推迟到更好的时间)。
  • 允许函数所有者为函数指定关键性级别。

虽然公共云可能无法像 XFaaS 一样在同一流程中运行来自不同用户的函数,但大型云客户可以在其虚拟私有云中采用 XFaaS 方法。


一个重要的有关上下文背景的启示:

  • XFaaS 的大多数函数都是由自动工作流触发的,因此可以接受延迟。
  • 同样,这也允许 XFaaS 跨时间(通过延迟函数)和跨空间(通过将其发送到负载较轻的数据中心)平衡负载。
  • XFaaS 支持 PHP、Python、Erlang、Haskell 的运行时,以及适用于任何语言的基于容器的通用运行时。

开发人员可为函数设置多个属性,如函数名称、参数、运行时间、关键性、执行开始时间、执行完成截止时间、资源配额、并发限制和重试策略。执行完成截止时间从几秒到 24 小时不等。

最后,XFaaS 在不同地区的硬件容量各不相同,因此负载平衡必须考虑到这一点。

工作负载类型
Meta 在 XFaaS 上有三种类型的工作负载:

  1. 队列触发式函数、
  2. 事件触发式函数(来自数据仓库和数据流系统中的数据变化事件)和
  3. 定时器触发式函数(在预先设定的时间自动启动)。

XFaaS 用于非面向用户的函数,如同步推荐系统、日志、生产力机器人、通知等。

调用架构

  • 客户端通过向提交器发送请求来启动函数调用,提交器在将请求传递给队列负载平衡器(QueueLB)之前会应用速率限制。
  • QueueLB 会将函数调用引导至 DurableQ 进行存储,直至完成。
  • 调度程序会定期将这些调用移至 FuncBuffer(内存中的函数缓冲区),并根据关键性、截止日期和配额对其进行排序。
  • 准备执行的调用会转移到 RunQ,然后 WorkerLB(工作者负载平衡器)会将它们分派给合适的工作者。
  • 提交者、QueueLB、调度器和 WorkerLB 都是无状态、无分隔和复制的,没有指定的领导者,因此它们的副本扮演着同等的角色。
  • DurableQ 是有状态的,并有一个分片的高可用数据库。
  • 只需添加更多副本,就能轻松扩展无状态组件。如果无状态组件出现故障,它的工作可以由对等组件接管。

中央控制器和容错
中央控制器独立于函数执行路径。它们通过更新关键配置参数来持续优化系统,而这些参数会被工作站和调度器等关键路径组件消耗。

这些配置缓存在组件中,因此如果中央控制器发生故障,系统也能正常运行(但无法重新配置)。

这样,中央控制器的宕机时间就只有几十分钟。这是 Meta 在系统中建立弹性的一个例子。


数据隔离

  • 为了安全或性能而需要强隔离的函数会被分配到不同的命名空间,每个命名空间使用不同的 Worker 池来实现物理隔离。
  • 由于私有云内部的信任、强制同行审查和现有的安全措施,多个函数可以在单个 Linux 进程中运行。数据只能从较低的分类级别流向较高的分类级别。

提交器

  • 客户端向提交器发送调用。提交器通过批处理调用并将其作为一个操作写入 DurableQ 来提高效率。
  • 提交器通过分布式键值存储管理大型参数存储,并具有内置的速率限制策略。为了管理客户提交率的变化,各地区为普通客户和高频客户设置了两套提交器。
  • XFaaS 会主动监控高频客户端,需要人工干预才能进行节流或服务级别目标(SLO)调整。

QueueLB 和 DurableQ
然后,提交者向 QueueLB 发送函数调用。Configerator(配置管理系统中央控制器)向 QueueLB 提供路由策略,以平衡不同区域的负载,因为 DurableQ 的硬件容量各不相同。

对 DurableQ 的调用使用 UUID 进行分片,以实现平均分配。每个 DurableQ 按照调用者设定的预定执行时间对函数调用进行分类和存储。

调度程序会不断查询 DurableQs 中存储的到期函数调用。当一个函数调用被 DurableQ 移交给调度程序时,除非执行失败,否则该函数调用将被该调度程序独占。

调度程序与 DurableQ 通信:

  • 发送成功执行的 ACK 消息。然后从 DurableQ 中永久删除函数调用。
  • 不成功则发送 NACK。该函数调用将再次进入 DurableQ,供其他调度程序处理。

调度程序
调度程序的主要职责是根据函数调用的重要性、截止日期和容量配额确定其优先级。

它从 DurableQs 中获取函数调用,将其合并到 FuncBuffers 中(按重要性排序,然后是执行截止日期),然后将其安排到 RunQ 中执行。FuncBuffers 和 RunQ 都是内存数据结构。

为了管理高效执行并防止系统滞后,RunQ 控制着函数调用流。该系统还确保负载平衡,由全球流量指挥器(中央控制器)根据需求和容量引导跨区域的函数调用流量,全面优化工人的利用率。

WorkerLB 和Workers
调度器的 RunQ 会将函数发送到 WorkerLB(工人负载平衡器),后者的函数由工人池中的worker运行。

在 XFaaS 系统中,使用相同编程语言的函数具有隔离性,有专用的运行时和worker工人池。

该系统的设计目标是让任何worker都能立即执行函数,而不会出现任何初始延迟。XFaaS 维护始终处于活动状态的运行时,并在本地固态硬盘上保持函数代码的更新。

合作即时编译(JIT)以减少开销
XFaaS 引入了合作 JIT 编译,定期将功能代码捆绑并推送给worker。由于代码会经常更新,这一点很有必要。

JIT 编译有三个执行阶段:

  • 少数worker测试新代码。
  • 2% 的worker进一步测试代码;部分worker为 JIT 编译进行剖析。
  • JIT 编译会在接收函数调用之前完成,从而避免了延迟。

在合作 JIT 编译的情况下,worker工人在函数代码更新后只需 3 分钟就能达到每秒最大请求数,而不进行 JIT 编译则需要 21 分钟。


高效内存消耗的位置组
内存限制使得存储每个函数的 JIT 代码变得不可行。

  • Locality Optimizer(中央控制器)将函数和 Worker 分为若干组,确保耗费内存的函数分布在各组中。
  • 使用局部性群组可将内存消耗减少 11-12%,并使worker能够高效、一致地利用内存。


XFaaS 如何高效处理负载峰值
函数配额:每个函数都有一个配额,由其所有者设定,该配额定义了每秒的 CPU 周期。该配额转化为每秒请求数(RPS)速率限制。中央速率限制器会根据函数的 RPS 限制来决定是否对函数调用进行节流。

时移计算:XFaaS 提供预留配额(用于迅速执行)和机会配额(用于在低需求时段的 24 小时内执行)。利用率控制器会根据工人利用率动态调整函数调用率。

函数的动态 RPS 限制:为防止下游服务负担过重,XFaaS 利用类似 TCP 的 AIMD(加法增加,乘法减少)方法动态调整 RPS 限制。它可以设置并发水平,并使用慢速启动方法管理 RPS 转移。

过去的挑战
如 XFaaS 功能超载 TAO 数据库导致服务级联故障,突出了这些保障措施的必要性。现在,XFaaS 已经证明了自己在保护下游服务方面的能力,在实际事件中,XFaaS 的后压机制预先遏制了潜在的过载,确保了服务的稳定性。