如何提高从 AWS Lambda (Java) 初始调用 AWS 服务的性能? [英] How to improve performance of initial calls to AWS services from an AWS Lambda (Java)?

查看:76
本文介绍了如何提高从 AWS Lambda (Java) 初始调用 AWS 服务的性能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I recently tried to analyze some performance issues on a service hosted in AWS Lambda. Breaking down the issue, I realized that it was only on the first calls on each container. When isolating the issue, I found myself creating a new test project to get a simple example.

测试项目(您可以克隆它,构建它mvn package,部署它sls deploy,然后通过 AWS 管理控制台进行测试.)

Test project (You can clone it, build it mvn package, deploy it sls deploy and then test it via the AWS Management Console.)

该项目有 2 个 AWS Lambda 函数:sourcetarget.target 函数只返回一个空的 json {}.source 函数使用 AWS Lambda SDK 调用 target 函数.

This project has 2 AWS Lambda functions: source and target. The target function simply returns an empty json {}. The source function invokes the target function using the AWS Lambda SDK.

target 函数的大约持续时间在冷启动时为 300-350 毫秒,在热调用时为 1 毫秒.source 函数的大致持续时间在冷启动时为 6000-6300 毫秒,在热调用时为 280 毫秒.

The approximate duration of the target function is 300-350 ms on cold starts and 1ms on hot invokes. The approximate duration of the source function is 6000-6300ms on cold starts and 280ms on hot invokes.

source 函数冷启动的 6 秒开销似乎是获取客户端的 3 秒和调用另一个函数的 3 秒,在热调用中分别为 3 毫秒和 250 毫秒.我在 AWS SNS 等其他服务上获得了类似的时间.

The 6 seconds overhead on the cold starts of the source function appear to be 3 seconds of getting the client and 3 seconds of invoking the other function, in hot invokes that is 3ms and 250ms respectively. I get similar times for other services like AWS SNS.

我真的不明白这 6 秒内它在做什么以及我能做些什么来避免它.在进行热身调用时,我可以获取客户端并存储引用以避免前几秒钟,但其他几秒钟来自实际使用其他服务(SNS、Lambda 等),我无法真正做到这一点无操作.

I don't really understand what it is doing in those 6 seconds and what I can do to avoid it. When doing warmup calls, I can get the client and store the reference to avoid the first few seconds, but the other few seconds come from actually using the other service (SNS, Lambda, etc), which I can't really do as a no-op.

那么,其他人是否经历过相同的冷启动持续时间,我可以做些什么来提高其性能?(除了带内存设置)

So, do other people experience the same cold start durations and what can I do to increase the performance on that? (other than bringing the memory setting up)

推荐答案

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

The main reason for slow cold-start times with a Java Lambda is the need to load classes and initialize objects. For simple programs this can be very fast: a Lambda that does nothing other than print "Hello, World" will run in ~40 ms, which is similar to the Python runtime. On the other hand, a Spring app will take much more time to start up, because even a simple Spring app loads thousands of classes before it does anything useful.

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

While the obvious way to reduce your cold-start times is to reduce the number of classes that you need to load, this is rarely easy to do, and often not possible. For example, if you're writing a web-app in Spring there's no way around initializing the Spring application context before processing a web request.

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

If that's not an option, and you're using the Maven Shade plugin to produce an "uber-JAR", you should switch to the Assembly plugin as I describe here. The reason is that Lambda unpacks your deployment bundle, so an "uber-JAR" turns into lots of tiny classfiles that have to be individually opened.

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

Lastly, increase your memory allotment. This without question the best thing that you can do for Lambda performance, Java or otherwise. First, because increasing memory reduces the amount of work that the Java garbage collector has to do. Second, because the amount of CPU that your Lambda gets is dependent on the memory allotment. You don't get a full virtual CPU until 1,769 MB. I recommend that for a Java app you give it 2 GB; the cost of the bigger allotment is often offset by reduced CPU requirements.

不会做的一件事是为预置的并发付费.如果您希望机器始终启动并运行,请使用 ECS/EKS/EC2.并认识到,如果您的需求激增,您仍然会遇到冷启动.

One thing I would not do is pay for provisioned concurrency. If you want a machine up and running all the time, use ECS/EKS/EC2. And recognize that if you have a bump in demand, you're still going to get cold starts.

更新:我在假期里花了一些时间来量化各种性能改进技术.完整的文章在这里,但这些数字值得重复.

Update: I spent some time over the holiday quantifying various performance improvement techniques. The full writeup is here, but the numbers are worth repeating.

我的示例程序与 OP 一样,是一个什么都不做"的程序.刚刚创建了一个 SDK 客户端并用它来调用 API:

My example program was, like the OP's, a "do nothing" that just created an SDK client and used it to invoke an 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));
}

我用不同的内存大小运行它,每次都强制冷启动.所有时间都以毫秒为单位:

I ran this with different memory sizes, forcing a cold start each time. All times are in milliseconds:

|                   |  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 越多越好.

As I said above, the primary benefit that you get from increasing memory is that you increase CPU at the same time. Creating and initializing an SDK client is CPU-intensive, so the more CPU you can give it, the better.

更新 2:今天早上我尝试使用 GraalVM.构建独立的可执行文件花了几分钟时间,即便如此,它还是创建了一个后备映像".(具有嵌入式 JDK)由于 AWS 开发工具包的依赖性.当我比较运行时,使用标准 Java 运行之间没有区别.

Update 2: this morning I tried compiling a simple AWS program with GraalVM. It took several minutes to build the stand-alone executable, and even then it created a "fallback image" (which has an embedded JDK) due to dependencies of the AWS SDK. When I compared runtimes, there was no difference between running with standard Java.

底线:将 Java 用于运行时间足以从 Hotspot 中受益的事物.使用不同的语言(Python、JavaScript,也许是 Go)来处理运行时间短且需要低延迟的事情.

Bottom line: use Java for things that will run long enough to benefit from Hotspot. Use a different language (Python, JavaScript, perhaps Go) for things that are short-running and need low latency.

这篇关于如何提高从 AWS Lambda (Java) 初始调用 AWS 服务的性能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆