Java Lambda 冷启动速度慢的主要原因是需要加载类和初始化对象。对于简单的程序,这可能非常快:除了打印“Hello, World”之外什么都不做的 Lambda 将在大约 40 毫秒内运行,这与 Python 运行时类似。另一方面,一个 Spring 应用程序需要更多的时间来启动,因为即使是一个简单的 Spring 应用程序在它做任何有用的事情之前都会加载数千个类。
虽然减少冷启动时间的明显方法是减少需要加载的类的数量,但这很难做到,而且通常是不可能的。例如,如果您在 Spring 中编写 Web 应用程序,则无法在处理 Web 请求之前初始化 Spring 应用程序上下文。
如果这不是一个选项,并且您正在使用 Maven Shade 插件来生成“uber-JAR”,那么您应该切换到我在此处描述的 Assembly 插件。原因是 Lambda 解压缩了您的部署包,因此“uber-JAR”变成了许多必须单独打开的小类文件。
最后,增加你的内存分配。毫无疑问,这是您可以为 Lambda 性能、Java 或其他方式做的最好的事情。首先,因为增加内存减少了 Java 垃圾收集器必须做的工作量。其次,因为您的 Lambda 获得的 CPU 数量取决于内存分配。直到 1,769 MB,您才能获得完整的虚拟 CPU。我建议为 Java 应用程序提供 2 GB 的空间;较大分配的成本通常会被减少的 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:今天早上我尝试用GraalVM编译一个简单的 AWS 程序。构建独立可执行文件需要几分钟时间,即使如此,由于 AWS 开发工具包的依赖性,它还创建了一个“后备映像”(具有嵌入式 JDK)。当我比较运行时时,使用标准 Java 运行没有区别。
底线:将 Java 用于运行时间足够长以受益于 Hotspot 的事物。对运行时间短且需要低延迟的事情使用不同的语言(Python、JavaScript,也许是 Go)。