AWS Lambda SnapStart Configuration

SnapStart 是一种快照和恢复机制,能够显著降低 AWS 上 Java 函数的冷启动时间。本文档说明了您可以使用此功能的各种设置。它并非 SnapStart 的参考文档,不会详细介绍 SnapStart 的工作原理。

SnapStart is a snapshotting and restore mechanism reducing drastically the cold startup time of Java functions on AWS. This document explains the various settings you can use to leverage this feature. It is not a reference documentation on SnapStart, and it will not cover how SnapStart works in details.

此功能仅适用于 AWS Lambda,且无法在所有区域内使用。请查阅 AWS 文档以验证您的 AWS 区域是否符合条件。

This feature is only available on AWS Lambda, and not in all regions. Please check the AWS documentation to verify the eligibility of your AWS region.

Enabling / Disabling SnapStart Optimizations

如果您使用 Quarkus AWS Lambda 扩展,SnapStart 优化将自动启用。但是,您可以使用以下命令明确启用/禁用它:

If you use the Quarkus AWS Lambda extension, SnapStart optimizations are automatically enabled. However, you can enable/disable it explicitly using:

quarkus.snapstart.enable=true|false

这不会为您的函数启用/禁用 SnapStart,只会启用/禁用 Quarkus 优化。

It does not enable/disable SnapStart for your function, only the Quarkus optimizations.

Class Preloading

类加载会对函数执行时间产生重大影响。此优化功能允许在 SnapStart 的快照处理过程中预加载类。

Classloading has a huge impact on your function execution time. This optimization allows preloading classes during the snapshotting process of SnapStart.

需要预加载的类在两个位置列出:

The classes to preload are listed in two places:

  1. extensions can produce a list of classes (using the io.quarkus.deployment.builditem.PreloadClassBuildItem build item)

  2. you can add a src/main/resources/META-INF/quarkus-preload-classes.txt file listing the classes to preload, such as:

com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal
com.fasterxml.jackson.annotation.JsonAlias
com.fasterxml.jackson.annotation.JsonFormat$Feature
com.fasterxml.jackson.core.exc.InputCoercionException
com.fasterxml.jackson.core.exc.StreamWriteException
com.fasterxml.jackson.core.io.ContentReference
com.fasterxml.jackson.core.io.IOContext
com.fasterxml.jackson.core.io.JsonEOFException
com.fasterxml.jackson.core.io.MergedStream
com.fasterxml.jackson.core.io.NumberInput
com.fasterxml.jackson.core.io.NumberOutput
com.fasterxml.jackson.core.io.UTF32Reader
com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper
com.fasterxml.jackson.core.json.JsonReadContext
com.fasterxml.jackson.core.json.JsonWriteContext
com.fasterxml.jackson.core.json.UTF8StreamJsonParser
com.fasterxml.jackson.core.JsonEncoding
com.fasterxml.jackson.core.JsonGenerationException
com.fasterxml.jackson.core.JsonLocation
com.fasterxml.jackson.core.JsonStreamContext
com.fasterxml.jackson.core.JsonToken
...

格式很简单:每行一个类。

The format is simple: one class per line.

Computing the class list

此步骤不友好。我们计划对此进行改进。

That step is particularly not user-friendly. We plan to improve it.

为了计算类列表,我们建议您部署函数并设置 JAVA_TOOL_OPTIONS 环境变量为 -verbose:class。然后执行函数并检索日志(在 CloudWatch 中)。您应该能够使用 sed/awk 或任何文本编辑器提取类名称。

To compute the list of classes, we recommend deploying your function and setting the JAVA_TOOL_OPTIONS environment variable to -verbose:class. Then execute your function and retrieve the log (in CloudWatch). You should be able to extract the class names using sed/awk or any text editor.

Application class list

默认情况下,Quarkus 会生成应用程序中包含类列表(包括由 Quarkus 生成的类)。因此,您不必在 quarkus-preload-classes.txt 文件中重复它们。

By default, Quarkus generates the class list of the classes included in your application (including the classes generated by Quarkus). So, you do not have to repeat them in the quarkus-preload-classes.txt file.

您可以使用以下步骤禁用此功能:

You can disable this feature using:

quarkus.snapstart.generate-application-class-list=false

Disable preloading

您可以使用以下步骤禁用类的预加载:

You can disable the preloading of classes using:

quarkus.snapstart.preload-classes=false

Skipping class initialization

默认情况下,当类被预加载时,它们也是 initialized,这意味着它还会解决依赖项类。你可以使用以下方法禁用此行为:

By default, when the classes are preloaded, they are also initialized, meaning it also resolves the dependent classes. You can disable this behavior using:

quarkus.snapstart.initialize-classes=false

Client Priming

客户端 _priming_是一种技术,允许在快照过程中初始化客户端,这样在应用程序运行时,该客户端已经完全具有功能。

Client priming is a technique that allows initializing a client during the snapshotting process, so it’s already fully functional during the application runtime.

有两种方式实现 priming

There are two ways to achieve priming:

  1. initialize the client in a static block, which, thanks to class preloading will be executed before the snapshot

  2. register a CRaC Resource doing the initialization

(1) 可以通过以下方式实现:

(1) can be achieved as follows:

@ApplicationScoped
public class HeroRepository {
    private static final DynamoDbClient client;

    static {
        client = DynamoDbClient.builder()
                .region(Region.US_EAST_2)
                .credentialsProvider(DefaultCredentialsProvider.create())
                .build();
        client.describeEndpoints();
    }
    // ...
}

使用静态块实现预处理可能阻止你的应用程序的本机编译。客户端初始化可能会启动线程或打开连接,如果该类在构建时初始化,则这些行为与本机编译不兼容。

Implementing priming using a static block may prevent the native compilation of your application. Client initialization may start threads or open connections which are not compatible with the native compilation if the class is initialized at build time.

下一部分涵盖 (2)。

The next section covers (2).

Resource registration

SnapStart 使用 CRaC API 允许应用程序在快照之前或还原期间执行自定义代码。

SnapStart uses the CRaC API to allow the application to execute custom code before the snapshotting or during the restoration.

虽然它是 CRaC API,但 SnapStart 并不是 CRaC,并且可以执行 things,而其他 CRaC 实现则不会。

While it’s the CRaC API, SnapStart is not CRaC and can do things that would not work with others CRaC implementations.

package org.acme.hello;

import io.quarkus.runtime.Startup;
import org.crac.Context;
import org.crac.Core;
import org.crac.Resource;
import org.jboss.logging.Logger;

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@Startup
@ApplicationScoped
public class HelloPriming implements Resource {

    @Inject
    Logger logger;

    @PostConstruct
    void init() {
        // Important - register the resource
        Core.getGlobalContext().register(this);
    }

    @Override
    public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
        logger.info("before checkout hook");
        // initialize your client here.
    }

    @Override
    public void afterRestore(Context<? extends Resource> context) throws Exception {
        logger.info("after checkout hook");
        // if there is anything to do during the restoration, do it here.
    }
}

还原时间限制为 2 秒。

Restoration is limited to 2 seconds.

TieredCompilation

当使用 SnapStart 时,还建议使用 tiered compilation。要实现这一点,将 JAVA_TOOL_OPTIONS`环境属性设为 `-XX:+TieredCompilation -XX:TieredStopAtLevel=1

It is also recommended to use tiered compilation when using SnapStart. To achieve this, set the JAVA_TOOL_OPTIONS environment property to -XX:+TieredCompilation -XX:TieredStopAtLevel=1.

`TieredCompilation`对于普通 Lambda 函数也可能很有用。

TieredCompilation can also be interesting for regular Lambda functions.