如何提高AWS Lambda(Java)对AWS服务的初始调用的性能?

如何提高AWS Lambda(Java)对AWS服务的初始调用的性能?,java,amazon-web-services,aws-lambda,serverless-framework,cold-start,Java,Amazon Web Services,Aws Lambda,Serverless Framework,Cold Start,我最近试图分析AWS Lambda中托管的服务的一些性能问题。 分解这个问题,我意识到这只是在每个容器上的第一次调用。 在隔离这个问题时,我发现自己创建了一个新的测试项目来获得一个简单的示例 您可以克隆它,构建它的mvn包,部署它的sls部署,然后通过AWS管理控制台进行测试 这个项目有两个AWS Lambda函数:源和目标。 目标函数只返回一个空的json{}。 源函数使用AWS Lambda SDK调用目标函数 目标函数的大致持续时间在冷启动时为300-350毫秒,在热调用时为1毫秒。 源

我最近试图分析AWS Lambda中托管的服务的一些性能问题。 分解这个问题,我意识到这只是在每个容器上的第一次调用。 在隔离这个问题时,我发现自己创建了一个新的测试项目来获得一个简单的示例

您可以克隆它,构建它的mvn包,部署它的sls部署,然后通过AWS管理控制台进行测试

这个项目有两个AWS Lambda函数:源和目标。 目标函数只返回一个空的json{}。 源函数使用AWS Lambda SDK调用目标函数

目标函数的大致持续时间在冷启动时为300-350毫秒,在热调用时为1毫秒。 源函数的大致持续时间在冷启动时为6000-6300ms,在热调用时为280ms

源函数冷启动的6秒开销似乎是获取客户端的3秒和调用另一个函数的3秒,热调用分别为3毫秒和250毫秒。 我得到类似的时间为其他服务,如AWS SNS

我真的不明白它在这6秒钟里做了什么,我能做些什么来避免它。 在进行热身呼叫时,我可以获取客户机并存储引用以避免前几秒钟,但其他几秒钟来自实际使用其他服务SNS、Lambda等,我不能真正做到这一点


那么,其他人是否经历了相同的冷启动持续时间?我可以做些什么来提高这方面的性能?除了设置内存外,配置的并发性还有助于缩短代码初始化的持续时间。除此之外,它还针对函数代码的执行环境设置带来的另一项开销


请参阅打开配置的并发部分。

配置的并发有助于缩短代码初始化的持续时间。除此之外,它还针对函数代码的执行环境设置带来的另一项开销


请参阅打开配置的并发部分。

Java Lambda冷启动时间慢的主要原因是需要加载类和初始化对象。对于简单的程序,这可能非常快:一个Lambda除了打印Hello,World之外什么都不做,它将在大约40毫秒内运行,这与Python运行时类似。另一方面,一个Spring应用程序需要花费更多的时间来启动,因为即使是一个简单的Spring应用程序在做任何有用的事情之前也会加载数千个类

虽然减少冷启动时间的明显方法是减少需要加载的类的数量,但这很少容易做到,而且常常是不可能的。例如,如果您在Spring中编写web应用程序,那么在处理web请求之前无法初始化Spring应用程序上下文

如果这不是一个选项,并且您正在使用Maven Shade插件来生成uber JAR,那么您应该切换到我描述的组装插件。原因是Lambda解压了您的部署包,因此一个uberjar会变成许多必须单独打开的小类文件

最后,增加你的内存分配。毫无疑问,这是提高Lambda性能、Java或其他方面所能做的最好的事情。首先,因为增加内存可以减少Java垃圾收集器必须完成的工作量。第二,因为。直到1769 MB,您才获得完整的虚拟CPU。我建议对于Java应用程序,你给它2GB;更大分配的成本通常被减少的CPU需求所抵消

我不愿意做的一件事是为提供的并发性付费。如果您想让机器一直运行,请使用ECS/EKS/EC2。并且要认识到,如果你的需求激增,你仍然会得到冷启动

更新:我在假期中花了一些时间来量化各种性能改进技术。完整的书写是,但数字值得重复

我的示例程序与OP的类似,是一个什么都不做的程序,它只是创建了一个SDK客户端,并使用它来调用API:

public void handler(Object ignored, Context context)
{
    long start = System.currentTimeMillis();
    
    AWSLogs client = AWSLogsClientBuilder.defaultClient();
    
    long clientCreated = System.currentTimeMillis();
    
    client.describeLogGroups();
    
    long apiInvoked = System.currentTimeMillis();
    
    System.err.format("time to create SDK client = %6d\n", (clientCreated - start));
    System.err.format("time to make API call     = %6d\n", (apiInvoked - clientCreated));
}
我用不同的内存大小运行它,每次都强制冷启动。所有时间均以毫秒为单位:

|                   |  512 MB | 1024 MB | 2048 MB | 4096 MB |
|+++++++++++++++++++|+++++++++|+++++++++|+++++++++|+++++++++|
| Create client     |    5298 |    2493 |    1272 |    1019 |
| Invoke API call   |    3844 |    2023 |    1061 |     613 |
| Billed duration   |    9213 |    4555 |    2349 |    1648 |
正如我上面所说的,增加内存的主要好处是同时增加CPU。创建和初始化SDK客户机是CPU密集型的,因此您可以为其提供的CPU越多越好

更新2:今天早上我试着用编译一个简单的AWS程序。构建独立的可执行文件需要几分钟的时间,而且由于AWS SDK的依赖性,它创建了一个具有嵌入式JDK的回退映像。当我比较运行时时,使用标准Java运行之间没有区别

一句话:将Java用于运行时间足以从Hotspot中获益的事情。使用不同的语言Python、JavaScript、pe
Rhap适用于运行时间短且需要低延迟的情况。

Java Lambda冷启动时间慢的主要原因是需要加载类和初始化对象。对于简单的程序,这可能非常快:一个Lambda除了打印Hello,World之外什么都不做,它将在大约40毫秒内运行,这与Python运行时类似。另一方面,一个Spring应用程序需要花费更多的时间来启动,因为即使是一个简单的Spring应用程序在做任何有用的事情之前也会加载数千个类

虽然减少冷启动时间的明显方法是减少需要加载的类的数量,但这很少容易做到,而且常常是不可能的。例如,如果您在Spring中编写web应用程序,那么在处理web请求之前无法初始化Spring应用程序上下文

如果这不是一个选项,并且您正在使用Maven Shade插件来生成uber JAR,那么您应该切换到我描述的组装插件。原因是Lambda解压了您的部署包,因此一个uberjar会变成许多必须单独打开的小类文件

最后,增加你的内存分配。毫无疑问,这是提高Lambda性能、Java或其他方面所能做的最好的事情。首先,因为增加内存可以减少Java垃圾收集器必须完成的工作量。第二,因为。直到1769 MB,您才获得完整的虚拟CPU。我建议对于Java应用程序,你给它2GB;更大分配的成本通常被减少的CPU需求所抵消

我不愿意做的一件事是为提供的并发性付费。如果您想让机器一直运行,请使用ECS/EKS/EC2。并且要认识到,如果你的需求激增,你仍然会得到冷启动

更新:我在假期中花了一些时间来量化各种性能改进技术。完整的书写是,但数字值得重复

我的示例程序与OP的类似,是一个什么都不做的程序,它只是创建了一个SDK客户端,并使用它来调用API:

public void handler(Object ignored, Context context)
{
    long start = System.currentTimeMillis();
    
    AWSLogs client = AWSLogsClientBuilder.defaultClient();
    
    long clientCreated = System.currentTimeMillis();
    
    client.describeLogGroups();
    
    long apiInvoked = System.currentTimeMillis();
    
    System.err.format("time to create SDK client = %6d\n", (clientCreated - start));
    System.err.format("time to make API call     = %6d\n", (apiInvoked - clientCreated));
}
我用不同的内存大小运行它,每次都强制冷启动。所有时间均以毫秒为单位:

|                   |  512 MB | 1024 MB | 2048 MB | 4096 MB |
|+++++++++++++++++++|+++++++++|+++++++++|+++++++++|+++++++++|
| Create client     |    5298 |    2493 |    1272 |    1019 |
| Invoke API call   |    3844 |    2023 |    1061 |     613 |
| Billed duration   |    9213 |    4555 |    2349 |    1648 |
正如我上面所说的,增加内存的主要好处是同时增加CPU。创建和初始化SDK客户机是CPU密集型的,因此您可以为其提供的CPU越多越好

更新2:今天早上我试着用编译一个简单的AWS程序。构建独立的可执行文件需要几分钟的时间,而且由于AWS SDK的依赖性,它创建了一个具有嵌入式JDK的回退映像。当我比较运行时时,使用标准Java运行之间没有区别

一句话:将Java用于运行时间足以从Hotspot中获益的事情。使用不同的语言Python、JavaScript,可能需要运行时间短且需要低延迟的东西。

是一个独立的jar,没有依赖项,并且小于60K。它的构建正是为了减少Java Lambda的冷启动时间,虽然您可能需要检查AWS API文档以完成任务,但它的使用非常简单。我发现使用AWS SDK S3 jar时,我的冷启动时间大约为10秒,而使用这个轻量级客户端时,时间减少到4秒,这是分配了512MB内存。使用AWS SDK将2GB内存分配给Lambda会产生3.6s的冷启动时间,而使用轻量级客户端则会产生1s的冷启动时间

库正在进行https调用这一事实确实带来了2000个左右的类的加载,因此很难比1s快很多,除非有一些很酷的https库在这方面效率更高。

是一个独立的jar,没有依赖项,并且小于60K。它的构建正是为了减少Java Lambda的冷启动时间,虽然您可能需要检查AWS API文档以完成任务,但它的使用非常简单。我发现使用AWS SDK S3 jar时,我的冷启动时间大约为10秒,而使用这个轻量级客户端时,时间减少到4秒,这是分配了512MB内存。使用AWS SDK将2GB内存分配给Lambda会产生3.6s的冷启动时间,而使用轻量级客户端则会产生1s的冷启动时间


库正在进行https调用这一事实确实带来了2000个左右的类的加载,因此很难比1s快很多,除非有一些很酷的https库在这方面效率更高。

请参阅。我建议您使用AWS X-Ray。您的测试代码调用Lambda,这在确定Lambda代码性能不佳的原因时没有用处。Lambda中的代码在哪里?两个Lambda中的代码都在测试项目中。目标lambda只打印调用的对象,然后返回一个空的json对象{}。测试的主要目标是找出如何优化调用不同lambda函数的过程,因此

hink调用一个简单的hello world lambda是最准确的测试。我建议您使用AWS X-Ray。您的测试代码调用Lambda,这在确定Lambda代码性能不佳的原因时没有用处。Lambda中的代码在哪里?两个Lambda中的代码都在测试项目中。目标lambda只打印调用的对象,然后返回一个空的json对象{}。测试的主要目标是找出如何优化调用不同lambda函数的过程,因此我认为调用简单的hello world lambda是最准确的测试。因此,如果我理解正确,我遇到的延迟可能主要是加载类文件和处理它们,对吗?在这种情况下,一个更轻量级的AWS Lambda sdk包会有趣吗?在任何情况下,我都会尝试稍微修改一下打包,尝试一下汇编插件,看看还能得到什么结果。@wietrol-我不确定你会在哪里找到一个更轻量级的SDK,除非你想自己编写它。即使如此,现有的SDK还是相对轻量级的,但我敢打赌,增加分配给Lambda的RAM数量将远远超过您可以做的任何事情。我重读了你的问题,发现你不想增加内存。为什么?我增加RAM没有问题,我可能会选择3GB左右的内存,但我正在寻找其他方法来加速初始化过程。如果我可以将初始化时间缩短到1秒以下,我就可以忘记强制使用热容器,只是将RAM设置为10GB并不是我愿意选择的选项,因此我搜索优化选项我尝试使用graalvm制作一个带有本机图像的hello world应用程序,但发现很难做到这一点,我还看到了一篇关于使用较新的JavaSDK和在创建时指定更多客户端设置的帖子:但是性能仍然没有明显的变化。我决定暂时增加容器的内存/cpu容量。因此,如果我理解正确,我所经历的延迟可能主要是加载类文件和处理它们,对吗?在这种情况下,一个更轻量级的AWS Lambda sdk包会有趣吗?在任何情况下,我都会尝试稍微修改一下打包,尝试一下汇编插件,看看还能得到什么结果。@wietrol-我不确定你会在哪里找到一个更轻量级的SDK,除非你想自己编写它。即使如此,现有的SDK还是相对轻量级的,但我敢打赌,增加分配给Lambda的RAM数量将远远超过您可以做的任何事情。我重读了你的问题,发现你不想增加内存。为什么?我增加RAM没有问题,我可能会选择3GB左右的内存,但我正在寻找其他方法来加速初始化过程。如果我可以将初始化时间缩短到1秒以下,我就可以忘记强制使用热容器,只是将RAM设置为10GB并不是我愿意选择的选项,因此我搜索优化选项我尝试使用graalvm制作一个带有本机图像的hello world应用程序,但发现很难做到这一点,我还看到了一篇关于使用较新的JavaSDK和在创建时指定更多客户端设置的帖子:但是性能仍然没有明显的变化。我决定暂时增加容器的容量内存/cpu。配置的并发性肯定会有助于避免冷启动,但它不会解决问题,因为当函数实际使用(如中所示)、实际发布到SNS主题或实际调用另一个Lambda时,会发生冷启动,因此,无论是配置的并发还是thundra之类的工具都不能真正预热lambda容器。配置的并发肯定会有助于避免冷启动,但它不会解决问题,因为当函数实际使用(如中)、实际发布到SNS主题或实际调用另一个lambda时,会发生冷启动,因此,无论是提供的并发性还是thundra之类的工具都无法真正预热lambda容器。