首页>国内 > 正文

什么场景(不)适合使用Lambda

2022-03-23 09:45:37来源:Thoughtworks洞见

作者|杨航

Lambda是AWS推出的基于Function-as-a-Service(FaaS)的Serverless服务。我结合项目使用体验,发现Lambda不适合或者说不能独立支撑以下场景:

用户期望稳定的低延迟请求需要在多个函数间跳转可预期的大量调用

与此同时,Lambda和其它AWS服务结合起来能为以下场景提供良好的解决方案:

作为监听器异步响应Webhook (API Gateway + SQS + Lambda)处理需要延时执行或指定时间执行的任务 (Step Functions + SQS + Lambda)

Lambda仅支持单请求模式,可以考虑使用AWS的App Runner或者GCP的Cloud Run替代。

背景介绍

笔者参与的项目大量使用Lambda进行开发,Lambda所承担的角色包括:作为AppServer支撑前端功能、监听第三方系统的Webhook,作为后台程序执行批处理任务,等等。在使用过程中,笔者感觉Lambda并非万能良方,有其设计和功能上的限制,所以根据项目的使用情况和体验,梳理了Lambda适合和不适合的场景,分享给大家,供大家在技术选型时进行参考。

Lambda有什么限制单请求模式:一个实例一次只能处理一个请求,如果在处理完成前又有新的请求需要处理,Lambda需要创建一个新的实例来处理。体积:一个函数解压后体积不能超过250MB,硬性限制;在使用Lambda时务必注意控制依赖,避免无用的依赖增大体积,并将静态文件等从代码库中抽离。特别值得注意的是Lambda运行时自带了aws-sdk,除非需要指定SDK的版本,否则请勿将SDK打入部署包中。并发数量:默认的一个帐户的区域并发限制是1000,也就是说可以同时处理1000个请求;可向AWS提出申请扩展到上万。如果到达上限,新的请求会被节流。在大型项目中不同模块请务必使用不同的帐号,以隔离对并发的需求,避免单模块workload的波动影响到整个系统的稳定性。可以通过Reserved Concurrency来限制单个函数并发数量,但同时会削减未设置Reserved Concurrency函数的并发上限。超时时间:最大900秒的超时时间,不可更改;如果在Happy Path时也不能判断执行时间少于900秒,则需要拆分Lambda或者使用其它方案。工具:Lambda有特定的部署方式,需要工具来支持,才能保证完整的开发流程;可使用的工具包括CDK、SAM、Serverless等。Lambda的特点生命周期

Lambda作为一种Serverless的计算服务,一个很重要的特点就是按需创建实例,即在请求到来时创建实例来处理(冷启动)。当实例处理完成请求后,会保留一段时间,可以响应后续请求(热启动)。如果实例空闲超过一段时间,就会被Lambda回收(AWS未明确提及回收的等待时间)。AWS官方没有给出状态的标准名称,我们这里用非标准的术语来描述生命周期,如下图:

同步 vs 异步

Lambda的函数有同步和异步两种执行模式。在同步模式下,当我们执行函数时,Lambda会创建/复用实例,并等待实例执行完成后再返回结果;在异步模式下,Lambda会将请求加入队列并立即返回,然后在后台创建/复用实例进行处理。使用异步模式时可以设置重试次数,并且如果重试后仍然不能成功,可以通过设置将失败的请求发送到另外的地方,比如SNS的Topic。

很多AWS服务都能与Lambda进行集成,需要查文档来明确调用Lambda的方式,比如API Gateway是以同步模式调用Lambda,CloudWatch Event是以异步模式调用Lambda。

Lambda不适合的场景用户期望稳定的低延迟

基于Lambda的生命周期,当有请求需要处理时,如果此时无可用实例,Lambda会初始化一个新实例并使用,也就是冷启动。结合Lambda单请求模式的特点,意味着一定会出现相当数量的冷启动,请求的响应时间会掺杂着实例初始化时间,出现延迟的波动。以项目经验来看,一个不复杂的NodeJS实现的函数,启动时间大概在1-3秒区间内波动;这个区间数值来自于CloudWatch的日志输出,实际体感时间可能更长,这部分时间会直接暴露给调用方。所以当一个场景需要提供持续稳定的低延迟响应时,以同步方式调用Lambda并不合适。

顺带一提,实例的启动时间是很重要的,如有些传统Java应用启动就需要几分钟的,建议不要直接放上Lambda。

请求需要在多个实例间跳转

如果一个请求需要以同步的形式在多个实例中跳转,在最坏情况下,会成倍放大请求的延迟,并且成倍消耗并发数量。以项目经验为例,有一个API Gateway -> Function A -> Function B -> 第三方系统的访问链路,在测试环境(用的人少,流量波动大)中,从页面调用这个接口的时间基本上在8秒以上,有时会超过10秒,让客户怀疑系统的性能有问题。

以网状结构设计的微服务模式应用,服务之间需要频繁同步通信,放上Lambda需慎重。

可预期的大量调用

如果一个接口有大量的调用,那么基于Efficiency和Cost的考虑,Lambda未必是合适的选择。

从一般性原则来讲,如果一个接口存在大量调用,那么为每次调用分配一个独占的实例显然不是一种明智的选择,这样会显著放大单个实例的边际开销。这种情况下,增加单个实例同时能处理的调用数量,能够有效提高系统吞吐量,提升系统的整体效率。

从价格方面来考虑,Lambda使用的是基于调用次数计费的模型,当调用次数增长到一定的阈值以上,其成本有效性必定会低于基于使用资源时长计费的模型。让我们用一个虚拟的场景来对比Lambda和App Runner:假设有一个接口,每天有3个小时的繁忙时段处理600 RPS的调用,另有12个小时非繁忙时段处理60 RPS的调用,其余时间没有调用;每次调用持续时间500ms。两种服务的价格对比如下:

Lambda: 基于128M内存的配置,每天有600*60*60*3 + 60*60*60*12 = 9072000次调用,那么每月费用为$335.76。感兴趣的读者可以使用AWS Pricing Calculator自行计算。App Runner: 基于1 vCPU和2G内存的配置,假设每个实例可以同时处理60个请求,当超出60个请求后会创建新实例来处理。那么每天繁忙时段的花费是 $2.30,非繁忙时段的花费是$0.77,没有调用时段的费用是$0.34,每月总费用是$102。对费用详情感兴趣的读者请移步App Runner Pricing页面的Example3: High volume production app。Lambda适合的场景作为监听器异步响应Webhook

很多第三方系统提供Webhook来进行通知,并且一般Webhook的设计都是异步模式。这种场景可通过API Gateway,SQS和Lambda提供解决方案。

让我们按照AWS的5 Pillars来分析为什么这是一个良好的解决方案:

Reliability: API Gateway加上SQS能够保证足够的高可用性,并且提供稳定的低延迟,这对Webhook的监听器来说相当重要,在Webhook设计里,如果监听器不能在短时间内提供响应,可能会被认为是不健康的,导致对监听器进行限流或屏蔽。Performance Efficiency: 上述服务提供了足够的可扩展性,保证监听器能够应对较大流量的变化,一般情况下无需提前预测流量来准备基础设施。Cost Optimization: 上述服务都是Serverless的服务,能够做到按实际使用付费,而无需为基础设施付费。Security: API Gateway和SQS自动提供了HTTPS协议,保证数据传输安全;SQS和Lambda可通过IAM确保访问控制,API Gateway可通过Authorizer或API Key来进行访问控制。Operational Excellence: 上述设计可完全通过Infrastructure as Code进行部署,无需手动操作。处理需要延时执行或指定时间执行的任务

有时候一个任务需要等待一段时间之后才执行,或者到了一个特定的时间才执行,相比用一个Long-run的服务去定时扫描处理,Step Functions、SQS加上Lambda提供了一种更高效的解决方案。

前述的优点不再重复提及,这里补充一些对Step Functions的说明。Step Functions是AWS提供的Serverless的状态机服务,其中包含了等待的状态,最长可等待1年的时间;AWS保证了等待的可靠性。Step Functions结合Lambda,可以针对单个任务去设置处理时间,不再需要批量扫描处理任务。Step Functions按照状态变化收费,等待时状态并没有发生变化,无需付费,可有效降低费用开销。

写在最后

Serverless的特性决定了实例无法避免冷启动。Lambda支持同步和异步两种调用模式,以项目经验来看,同步调用模式受冷启动影响更大,有时会通过SQS将调用封装成异步模式。在Serverless工具中甚至提供了Serverless WarmUp Plugin插件,通过定时调用避免冷启动。AWS也提供了Provisioned Concurrency特性来维持热实例,减少冷启动的次数。

Lambda的单请求模式是一个很大的限制,既限制了实例的性能(比如使用NIO),又导致实例需要更频繁初始化。如果能够改变单请求模式,让一个实例接受更多的请求,将会是一个很好的特性。

Lambda有一套独立的生态系统,对代码和部署都有特定的要求,降低了代码可移植性。

有没有更好的选择呢?笔者推荐读者参考下GCP的Cloud Run服务,提供了Container-as-a-Service(CaaS)解决方案,能够将镜像以Serverless形式部署上去,通过指定实例的请求并发度,能显著减少初始化新实例的次数。AWS也提供了类似的服务App Runner,不过目前只在美国、爱尔兰和日本区域提供。

关键词: 解决方案 生命周期 一段时间 同时处理 基础设施

相关新闻

Copyright 2015-2020   三好网  版权所有