AWS Lambda SnapStart Configuration

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

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

Enabling / Disabling SnapStart Optimizations

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

quarkus.snapstart.enable=true|false

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

Class Preloading

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

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

  1. 扩展可以生成类列表(使用 io.quarkus.deployment.builditem.PreloadClassBuildItem 构建项)

  2. 您可以添加一个 src/main/resources/META-INF/quarkus-preload-classes.txt 文件来列出需要预加载的类,如下所示:

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
...

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

Computing the class list

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

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

Application class list

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

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

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

Disable preloading

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

quarkus.snapstart.preload-classes=false

Skipping class initialization

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

quarkus.snapstart.initialize-classes=false

Client Priming

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

有两种方式实现 priming

  1. 在 `static`块中初始化客户端,由于类预加载,这样会在快照之前执行

  2. 注册一个执行初始化工作的 CRaC 资源

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

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

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

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

下一部分涵盖 (2)。

Resource registration

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

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

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 秒。

TieredCompilation

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

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