如何保持所需数量的AWS Lambda函数容器保持“温热”

19
在我的项目中,有一组使用AWS API Gateway和AWS Lambda实现的REST API。由于AWS Lambda函数是无服务器且无状态的,当我们调用它时,AWS会启动一个包含Lambda函数代码的容器来处理我们的调用。根据AWS文档,在Lambda函数执行完成后,AWS不会停止容器,因此我们可以在该容器中处理下一个调用。这种方法提高了服务性能-只有在第一次调用时,AWS才花费时间启动容器(Lambda函数的冷启动),并且所有后续调用都比较快,因为它们使用相同的容器(热启动)。
为了进一步提高性能,我们创建了一个定期调用Lambda函数的cron作业(我们使用Cloudwatch规则进行)。这种方法使Lambda函数保持“温暖”,避免停止和重新启动容器。也就是说,当真正的用户调用我们的REST API时,Lambda将不会花费时间启动一个新的容器。

然而我们遇到了一个问题 - 这种方法只能保持Lambda函数的一个容器保持温暖,而来自不同用户的实际并行调用数量可能要大得多(在我们的情况下,这是数百甚至数千个用户)。有没有办法实现Lambda函数的预热功能,它可以使不止单个容器保持温暖,而是一些所需数量的容器?

我知道这种方法可能会影响使用Lambda函数的成本,并且可能最好使用老式的应用服务器,但比较这些方法及其成本将是接下来的步骤,我认为,在当前时刻,我只想找到预热所需数量的Lambda函数容器的方法。


4
很遗憾,关于这个主题并没有太多的文档资料。如果我们相信 AWS 文档,AWS Lambda 容器每次只能处理一个事件(请纠正我是否理解错误)。如果您有一个单一的 cron Lambda 函数,同时发送6个事件到API Lambda函数,那么我猜这会触发6个不同的 Lambda 容器。如果您认为值得尝试,请告诉我您的发现。 - Tom Nijs
2
@TomNijs 你说得对。一个容器永远不会处理多个并发调用。 - Michael - sqlbot
@Michael-sqlbot 但根据我在问题中提到的AWS文档,Lambda容器是可以被重复使用的。这不是真的吗? - Hleb
1
@GlebKosteiko 两种说法都是正确的;这只是术语问题:容器经常被重复使用,但“重复使用”并不意味着在同一时间(并发地)在给定的容器中运行多次函数调用。例如,如果您有一个需要15秒钟的函数,并且您现在和一分钟后调用它(没有其他人正在调用它),那么很可能它会在同一个容器中两次运行。但如果您现在运行它,5秒钟后再次运行它,那么这两次调用将永远不会在同一个容器中。重用不重叠。 - Michael - sqlbot
1
有点离题,你使用的是哪种语言,分配了多少内存?有些语言(如Java)启动速度比其他语言慢得令人痛苦,因此如果你愿意为提高性能付出一些代价,有时可以通过增加内存来缩短冷启动时间,因为这会将可用的CPU周期增加大约相同的倍数...例如,从128MB更改为512MB也意味着获得4倍的CPU数量,即使你实际上不需要更多的内存,而且添加的内存并不直接改变性能。如果不需要,也可以不使用VPC。 - Michael - sqlbot
显示剩余3条评论
5个回答

22
长,但请耐心阅读,这可能会为您提供解决方法,也可能会让您更好地理解 Lambda如何工作?

如果您不想阅读,请跳到底部的"解决方法"。

对于不了解冷启动的人,请阅读这篇博客文章以更好地理解。简单描述如下:

冷启动

  • 当某个函数第一次执行或在其代码或资源配置更新后执行时,将会 创建一个容器来执行此函数。所有代码和库将被加载到容器中以便它能够执行。 然后代码将运行,从初始化代码开始。初始化 代码是指处理程序外编写的代码。仅当容器首次创建时才运行此代码。 最后,Lambda处理程序被执行。这个设置过程就是所谓的冷启动。
  • 为了提高性能,Lambda有能力重复使用先前调用创建的容器。 这将避免初始化新容器和加载代码。仅会执行处理程序代码。 但是,您不能依赖于先前调用的容器被重用。如果您没有更改代码并且不太长,很长一段时间过去了,Lambda可能会重用之前的容器。如果更改了代码、资源配置或者之前的调用已经经过了一段时间,Lambda将初始化一个新的容器,你将遇到“冷启动”问题。现在考虑以下情形以便更好地理解:第一次调用示例中的Lambda函数时,Lambda将创建一个容器,将代码加载到容器中并运行初始化代码。然后执行函数处理程序。这个调用将会经历“冷启动”。如注释所述,函数需要15秒才能完成。一分钟后再次调用该函数。Lambda很可能会重用前面一次调用的容器。这个调用将不会经历“冷启动”。现在考虑第二种情况,第二次调用在第一次调用后5秒钟执行。由于上一个函数需要15秒才能完成并且尚未执行完毕,因此新的调用将必须为该函数创建一个新的容器来执行。因此,这个调用将会经历“冷启动”。

    关于避免冷启动,这是一种可能性,但不能保证成功。通常的解决方法只会保持Lambda函数中一个容器的状态。你可以通过运行一个CloudWatch事件来实现,使用计划事件(cron表达式)每隔几分钟调用您的Lambda函数以使其保持热备状态。


    解决方法:

    对于您的用例,您的Lambda函数将使用非常高的并发率频繁调用。为了尽可能避免冷启动,您需要保持尽可能多的容器处于活动状态,以应对最高并发量。为此,您需要在规定延迟时间内调用函数,以便该函数的并发数增加并达到所需的并发执行数量。这将强制Lambda启动您所需的容器数量。结果,这可能会增加成本,并且无法保证完全避免冷启动。

    话虽如此,以下是如何同时保持多个容器处于热备状态的详细说明:

    • 您应该有一个按计划触发的CloudWatch事件规则。该计划可以是固定速率或cron表达式,例如,您可以将此规则设置为每5分钟触发一次。然后,您将指定Lambda函数(控制器函数)作为此规则的目标

    • 您的控制器Lambda函数将会为您所需的并发运行容器 调用Lambda函数(要保持热状态的函数)。

    这里有几件事需要考虑:

    1. 需要构建并发,因为如果第一个调用完成之后,另一个调用开始之前,则此调用可能会重用先前调用的容器而不创建新的。为此,如果该函数由控制器函数调用,则需要在Lambda函数上添加某种延迟。 可以通过向这些调用的函数传递特定的负载来实现这一点。您希望保持热状态的Lambda函数将检查是否存在此负载。如果存在,则函数将等待(以构建并发调用),如果不存在,则函数可以按预期执行。

    2. 如果反复调用Invoke Lambda API,您还需要确保没有被限流。 如果出现限制流量,您的Lambda函数应编写以处理它,并考虑在API调用之间添加延迟以避免限流。

    最终此解决方案可以减少冷启动但它会增加成本,而且不能保证 Lambda 冷启动的必然性。如果您的应用程序需要更快的响应时间而不是 Lambda 冷启动所需的时间,我建议考虑将服务器放在 EC2 实例上。


4
我们正在使用Java(Spring Boot)lambda,并且基本上得出了与Kush Vyas上面的答案几乎相同的解决方案,这个方案非常好用。
然而,在负载测试期间我们发现在“控制器函数”执行期间经常发生合法用户请求,从而导致必然的冷启动...
因此,在我们的“控制器函数”中,除了常规数量的X并发预热请求外,我们每执行5次该函数就会额外调用目标lambda 2次。理论上,我们将最终有X+2个保持温暖的lambda,但在5次预热调用中仍将有2个多余的lambda可以为用户请求提供服务。
这确实进一步减少了我们的冷启动次数(但显然还不是完全),我们仍在尝试并发/预热/睡眠时间组合的频率以找到适合我们的最佳解决方案 - 这些值始终取决于特定情况下的负载要求。

3

1
如果您在AWS Lambda中使用无服务器框架,您可以使用此插件以一定的并发级别保持所有Lambda函数处于活动状态。

0
我想分享一个小而实用的技巧,我们用它来减少与冷启动相关的“用户观察”延迟。在我们的情况下,Lambda函数通过AWS API Gateway处理来自前端的HTTP请求,特别是当用户在输入框中键入内容时执行搜索功能。通常,用户在UI渲染后会有一些延迟才开始输入,所以我们有一些时间执行ping调用到我们的Lambda函数,使其变暖。当用户向后端发出请求时,Lambda很可能已经准备好工作。
实际上,这种方法对于修复后端冷启动问题没有任何帮助,您需要寻找其他解决方案,但这可以是一个用户体验改进,不需要太多努力(类似热补丁)。 有一件事情你应该记住 - 如果您的服务是公开的,并且您关心Google Insights分数,您应该小心实施这种方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接