Lambda的冷启动

冷启动带来的延迟

上一节我们了解到,当Lambda第一次收到调用请求时,会初始化execution environment,在这个阶段会下载函数代码(存放在AWS内部的S3桶中,如果使用镜像则从ECR中下载)、配置内存及函数运行时、运行handler外层代码。当完成这些准备工作后,才真正运行handler内部的代码:

                perf optimize figure 1

上图中,前两个阶段(下载代码、准备Execution environment)又统称为冷启动阶段。这个阶段虽然不会额外收费,但却为我们的函数执行带来了整体延迟。在第一次执行完后,Execution environment会保存一段时间,在这段时间内如果有后续的调用,则直接运行handler代码。

AWS通过对各个客户的Lambda调用模式进行统计,发现冷启动才占不到1%,冷启动所花时间也不尽相同,从100ms以下到大于1s都有。并且一般线上业务请求量较频繁,冷启动发生的概率更低;测试环境和开发环境发生概率相对较高。

花式触发冷启动的场景…

虽然说Lambda在第一次执行后,会保存execution environment , 但还有很多场景会触发冷启动:

  • Lambda为了保证高可用,在各个AZ都会运行,并且根据流量向这些AZ进行负载均衡。假设某一时刻Lambda在很短时间内被调用两次,那么这两次运行都有可能经历冷启动,因为它们被负载到两个AZ运行了…

  • 如果流量突增,Lambda会自动扩容,新扩容的函数也要初始化execution environment,所以都要再经历冷启动的过程。 例如同时并发调用6次Lambda,每个调用都要新初始化执行环境:

                    perf optimize figure 5

  • 每次更新Lambda函数代码或者更新它的配置,下一次调用时都会重新进行冷启动过程:

                perf optimize figure 2

为了尽量减少冷启动,一些人想出了定时调用Lambda的方式,例如通过EventBridge每隔一分钟调用一次Lambda来进行预热。不得不说,这种方式减少了执行Lambda时发生冷启动的概率,但由于负载均衡 + 自动扩容机制的存在,依然不能保证不发生冷启动。

通过Provisioned Concurrency减少冷启动

Provisioned Concurrency是比较推荐的减少冷启动的方式。在调用Lambda之前,Provisioned Concurrency帮我们完成了初始化的工作:

                perf optimize figure 3

例如,如果我们为一个Lambda函数配置了6个Provisioned Concurrency,那么当我们调用它之前,就提前准备好了6个execution environments,避免调用时发生冷启动过程:

                perf optimize figure 4

没有配置Provisioned Concurrency的Lambda又叫on-demand模式,和这种模式比起来,它有以下优点:

  • 不用考虑初始化代码的时间。如果我们使用的runtime需要花费比较长的时间来进行初始化(例如Java),那么使用Provisioned Concurrency能避免这段时间, 用户根本不用考虑对初始化代码进行优化。

注意Provisioned Concurrency不能在Lambda的 $LATEST版本上使用