

# 使用 Lambda SnapStart 提高启动性能
<a name="snapstart"></a>

Lambda SnapStart 可以提供低至次秒级的启动性能，通常无需更改函数代码。SnapStart 可以更轻松地构建响应速度快且可扩展的应用程序，而无需预调配资源或实施复杂的性能优化。

导致启动延迟的最大因素（通常称为冷启动时间）是指 Lambda 在初始化函数上花费的时间，其中包括加载函数代码、启动运行时以及初始化函数代码。通过 SnapStart，Lambda 可以在您发布函数版本时初始化您的函数。Lambda 会对已初始化的[执行环境](lambda-runtime-environment.md)的内存和磁盘状态创建 [Firecracker microVM](https://aws.amazon.com/blogs/opensource/firecracker-open-source-secure-fast-microvm-serverless/) 快照、加密该快照，并智能地对其进行缓存以优化检索延迟。

为了确保弹性，Lambda 会保留每个快照的多个副本。Lambda 使用最新的运行时和安全更新自动修补快照及其副本。当您首次调用某个函数版本时，以及随着调用的纵向扩展，Lambda 会从缓存的快照恢复新的执行环境，而不是从头开始初始化，从而减少了启动延迟。

**重要**  
如果您的应用程序要求状态唯一，则必须评估您的函数代码并验证其能否在快照操作后快速恢复。有关更多信息，请参阅 [使用 Lambda SnapStart 处理唯一性](snapstart-uniqueness.md)。

**Topics**
+ [何时使用 SnapStart](#snapstart-use-cases)
+ [支持的功能和限制](#snapstart-runtimes)
+ [支持的区域](#snapstart-supported-regions)
+ [兼容性注意事项](#snapstart-compatibility)
+ [SnapStart 定价](#snapstart-pricing)
+ [激活和管理 Lambda SnapStart](snapstart-activate.md)
+ [使用 Lambda SnapStart 处理唯一性](snapstart-uniqueness.md)
+ [在 Lambda 函数快照之前或之后实现代码](snapstart-runtime-hooks.md)
+ [监控 Lambda SnapStart](snapstart-monitoring.md)
+ [Lambda SnapStart 的安全模型](snapstart-security.md)
+ [最大限度地提高 Lambda SnapStart 的性能](snapstart-best-practices.md)
+ [排查 Lambda 函数的 SnapStart 错误](snapstart-troubleshooting.md)

## 何时使用 SnapStart
<a name="snapstart-use-cases"></a>

Lambda SnapStart 旨在解决由一次性初始化代码引入的延迟变化，例如加载模块依赖项或框架。在初始调用期间，这些操作有时候可能需要几秒钟才能完成。在最佳情况下，使用 SnapStart 将此延迟从几秒钟缩短至次秒级。SnapStart 在与大规模函数调用搭配使用时效果最佳。不经常调用的函数可能无法获得相同的性能改进。

SnapStart 对两种主要类型的应用程序尤为有益：
+ **延迟敏感型 API 和用户流：**作为关键 API 端点或面向用户的流的一部分，函数可以受益于 SnapStart 的延迟减少和响应时间的缩短。
+ **延迟敏感型数据处理工作流：**使用 Lambda 函数的限时数据处理工作流可以通过减少异常函数初始化延迟来实现更好的吞吐量。

[预置并发](provisioned-concurrency.md)可使函数保持初始化状态，并做好准备在几十毫秒内做出响应。如果您的应用程序具有严格的冷启动延迟要求，而 SnapStart 无法充分满足该要求，则请使用预调配并发。

## 支持的功能和限制
<a name="snapstart-runtimes"></a>

SnapStart 适用于以下 [Lambda 托管式运行时](lambda-runtimes.md)：
+ Java 11 及更高版本
+ Python 3.12 及更高版本
+ .NET 8 及更高版本。如果您使用的是[适用于.NET 的 Lambda 注释框架](csharp-handler.md#csharp-handler-annotations)，则请升级到 [Amazon.Lambda.Annotations](https://www.nuget.org/packages/Amazon.Lambda.Annotations) 1.6.0 版或更高版本，以确保与 SnapStart 兼容。

不支持其他托管运行时系统（例如 `nodejs24.x` 和 `ruby3.4`）、[仅限操作系统的运行时系统](runtimes-provided.md) 和[容器映像](images-create.md)。

SnapStart 不支持[预置并发](provisioned-concurrency.md)、[Amazon Elastic File System（Amazon EFS）](https://docs.aws.amazon.com/efs/latest/ug/accessing-fs.html)或大于 512MB 的短暂存储。

**注意**  
您只能对[已发布的函数版本](configuration-versions.md#configuration-versions-config)以及与版本关联的[别名](configuration-aliases.md)使用 SnapStart。不能对函数的未发布版本（\$1LATEST）使用 SnapStart。

## 支持的区域
<a name="snapstart-supported-regions"></a>

Lambda SnapStart 可在除亚太地区（新西兰）和亚太地区（台北）以外的所有[商业区域](https://docs.aws.amazon.com/general/latest/gr/glos-chap.html#region)使用。

## 兼容性注意事项
<a name="snapstart-compatibility"></a>

通过 SnapStart，Lambda 可以使用单个快照作为多个执行环境的初始状态。如果您的函数在[初始化阶段](lambda-runtime-environment.md#runtimes-lifecycle-ib)使用以下任何内容，则可能需要在使用 SnapStart 之前进行一些更改：

**独特性**  
如果您的初始化代码生成了唯一内容，而此内容包含在快照中，则跨执行环境重复使用该内容时，该内容可能不唯一。若要在使用 SnapStart 时保持唯一性，则必须在初始化后生成唯一的内容。其中包括唯一 ID、唯一密钥以及用于生成伪随机性的熵。要了解如何还原唯一性，请参阅 [使用 Lambda SnapStart 处理唯一性](snapstart-uniqueness.md)。

**网络连接**  
当 Lambda 从快照恢复您的函数后，无法保证您的函数在初始化阶段所建立连接的状态不变。验证您的网络连接状态，并在必要时重新建立连接。在大多数情况下，AWS 开发工具包建立的网络连接会自动恢复。关于其他连接，请查看[最佳实践](snapstart-best-practices.md)。

**临时数据**  
有些函数会在初始化阶段下载或初始化暂存数据，例如临时凭证或缓存的时间戳。使用暂存数据之前，请在函数处理程序中刷新此数据，即使不使用 SnapStart 也是如此。

## SnapStart 定价
<a name="snapstart-pricing"></a>

**注意**  
对于 Java 托管式运行时，SnapStart 不会产生额外成本。您需要根据对您的函数发出的请求数量、您的代码运行所用时间以及为您的函数配置的内存来支付相关费用。

使用 SnapStart 的成本包括以下内容：
+ **缓存：**对于在启用 SnapStart 的情况下发布的每个函数版本，您需要支付缓存和维护快照的成本。此价格取决于您为函数分配的[内存](configuration-memory.md)量。您至少要支付 3 小时的费用。只要您的函数保持[活动](snapstart-activate.md#snapstart-active)状态，就会继续向您收费。使用 [ListVersionsByFunction](https://docs.aws.amazon.com/lambda/latest/api/API_ListVersionsByFunction.html) API 操作识别函数版本，然后使用 [DeleteFunction](https://docs.aws.amazon.com/lambda/latest/api/API_DeleteFunction.html) 删除未使用的版本。要自动删除未使用函数版本，请参阅 Serverless Land 上的 [Lambda 版本清理](https://serverlessland.com/workflows/step-functions-lambda-version-cleanup)模式。
+ **还原：**每次从快照还原函数实例时，您都要支付还原费用。价格取决于您为函数分配的内存量。

与所有 Lambda 函数一样，持续时间收费适用于在函数处理程序中运行的代码。对于 SnapStart 函数，持续时间费用也适用于在处理程序之外声明的初始化代码、运行时加载所需的时间，以及在[运行时钩子](snapstart-runtime-hooks.md)中运行的任何代码。持续时间从代码开始运行开始计算，直到代码返回或以其他方式结束为止，并向上取整为最接近的 1 毫秒。Lambda 会维护快照的缓存副本以实现弹性，并自动应用软件更新，例如运行时升级和安全补丁。每次 Lambda 重新运行初始化代码以应用软件更新时，都会收取费用。

有关 SnapStart 的使用成本的更多信息，请参阅 [AWS Lambda 定价](https://aws.amazon.com/lambda/pricing/#SnapStart_Pricing)。

# 激活和管理 Lambda SnapStart
<a name="snapstart-activate"></a>

若要使用 SnapStart，请对新的或现有 Lambda 函数激活 SnapStart。然后，发布并调用一个函数版本。

**Topics**
+ [激活 SnapStart（控制台）](#snapshot-console)
+ [激活 SnapStart（AWS CLI）](#snapshot-cli)
+ [激活 SnapStart（API）](#snapshot-api)
+ [Lambda SnapStart 和函数状态](#snapstart-function-states)
+ [更新快照](#update-snapshot)
+ [将 SnapStart 与 AWS SDK 结合使用](#snapstart-credentials)
+ [将 SnapStart 与 CloudFormation、AWS SAM 和 AWS CDK 结合使用](#snapstart-cfn-sam)
+ [删除快照](#snapshot-delete)

## 激活 SnapStart（控制台）
<a name="snapshot-console"></a>

**为函数激活 SnapStart**

1. 打开 Lamba 控制台的[函数](https://console.aws.amazon.com/lambda/home#/functions)页面。

1. 选择一个函数的名称。

1. 选择 **Configuration**（配置），然后选择 **General configuration**（常规配置）。

1. 在 **General configuration**（常规配置）窗格上，选择 **Edit**（编辑）。

1. 在 **Edit basic settings**（编辑基本设置）页面上，为 **SnapStart** 选择 **Published versions**（已发布版本）。

1. 选择**保存**。

1. [发布函数版本](configuration-versions.md#configuration-versions-config)。Lambda 会初始化您的代码，创建初始化执行环境的快照，然后缓存快照以实现低延迟访问。

1. [调用函数版本](configuration-versions.md#versioning-versions-using)。

## 激活 SnapStart（AWS CLI）
<a name="snapshot-cli"></a>

**为现有函数激活 SnapStart**

1. 通过运行带有 **--snap-start** 选项的 [update-function-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-configuration.html) 命令来更新函数配置。

   ```
   aws lambda update-function-configuration \
     --function-name my-function \
     --snap-start ApplyOn=PublishedVersions
   ```

1. 使用 [publish-version](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/publish-version.html) 发布函数版本。

   ```
   aws lambda publish-version \
     --function-name my-function
   ```

1. 通过运行 [get-function-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/get-function-configuration.html) 命令并指定版本号，确认已为相应函数版本激活 SnapStart。以下示例指定版本 1。

   ```
   aws lambda get-function-configuration \
     --function-name my-function:1
   ```

   如果其响应显示 [OptimizationStatus](https://docs.aws.amazon.com/lambda/latest/api/API_SnapStartResponse.html) 为 `On` 且 [State](https://docs.aws.amazon.com/lambda/latest/api/API_GetFunctionConfiguration.html#lambda-GetFunctionConfiguration-response-State)（状态）为 `Active`，则表示 SnapStart 已激活，并且已为指定函数版本生成快照。

   ```
   "SnapStart": { 
       "ApplyOn": "PublishedVersions",
       "OptimizationStatus": "On"
    },
    "State": "Active",
   ```

1. 通过运行 [invoke](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/invoke.html) 命令并指定版本来调用相应的函数版本。以下示例调用版本 1。

   ```
   aws lambda invoke \
     --cli-binary-format raw-in-base64-out \
     --function-name my-function:1 \
     --payload '{ "name": "Bob" }' \
     response.json
   ```

   如果使用 **cli-binary-format** 版本 2，则 AWS CLI 选项是必需的。要将其设为默认设置，请运行 `aws configure set cli-binary-format raw-in-base64-out`。有关更多信息，请参阅*版本 2 的 AWS Command Line Interface 用户指南*中的 [AWS CLI 支持的全局命令行选项](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)。

**创建新的函数时激活 SnapStart**

1. 通过运行带有 **--snap-start** 选项的 [create-function](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html) 命令来创建函数。对于 **--role**，指定您的[执行角色](lambda-intro-execution-role.md)的 Amazon 资源名称（ARN）。

   ```
   aws lambda create-function \
     --function-name my-function \
     --runtime "java25" \
     --zip-file fileb://my-function.zip \
     --handler my-function.handler \
     --role arn:aws:iam::111122223333:role/lambda-ex \
     --snap-start ApplyOn=PublishedVersions
   ```

1. 使用 [publish-version](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/publish-version.html) 命令创建版本。

   ```
   aws lambda publish-version \
     --function-name my-function
   ```

1. 通过运行 [get-function-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/get-function-configuration.html) 命令并指定版本号，确认已为相应函数版本激活 SnapStart。以下示例指定版本 1。

   ```
   aws lambda get-function-configuration \
     --function-name my-function:1
   ```

   如果其响应显示 [OptimizationStatus](https://docs.aws.amazon.com/lambda/latest/api/API_SnapStartResponse.html) 为 `On` 且 [State](https://docs.aws.amazon.com/lambda/latest/api/API_GetFunctionConfiguration.html#lambda-GetFunctionConfiguration-response-State)（状态）为 `Active`，则表示 SnapStart 已激活，并且已为指定函数版本生成快照。

   ```
   "SnapStart": { 
        "ApplyOn": "PublishedVersions",
        "OptimizationStatus": "On"
     },
     "State": "Active",
   ```

1. 通过运行 [invoke](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/invoke.html) 命令并指定版本来调用相应的函数版本。以下示例调用版本 1。

   ```
   aws lambda invoke \
     --cli-binary-format raw-in-base64-out \
     --function-name my-function:1 \
     --payload '{ "name": "Bob" }' \
     response.json
   ```

   如果使用 **cli-binary-format** 版本 2，则 AWS CLI 选项是必需的。要将其设为默认设置，请运行 `aws configure set cli-binary-format raw-in-base64-out`。有关更多信息，请参阅*版本 2 的 AWS Command Line Interface 用户指南*中的 [AWS CLI 支持的全局命令行选项](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)。

## 激活 SnapStart（API）
<a name="snapshot-api"></a>

**激活 SnapStart**

1. 请执行以下操作之一：
   + 通过使用 [CreateFunction](https://docs.aws.amazon.com/lambda/latest/api/API_CreateFunction.html) API 操作并指定 [SnapStart](https://docs.aws.amazon.com/lambda/latest/api/API_SnapStart.html) 参数，创建已激活 SnapStart 的新函数。
   + 通过使用 [UpdateFunctionConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_UpdateFunctionConfiguration.html) 操作并指定 [SnapStart](https://docs.aws.amazon.com/lambda/latest/api/API_SnapStart.html) 参数，为现有函数激活 SnapStart。

1. 使用 [PublishVersion](https://docs.aws.amazon.com/lambda/latest/api/API_PublishVersion.html) 操作发布函数版本。Lambda 会初始化您的代码，为已初始化的执行环境创建快照，然后缓存该快照以实现低延迟访问。

1. 通过使用 [GetFunctionConfiguration](https://docs.aws.amazon.com/lambda/latest/api/API_GetFunctionConfiguration.html) 操作确认已为该函数版本激活 SnapStart。指定版本号以确认已为该版本激活 SnapStart。如果其响应显示 [OptimizationStatus](https://docs.aws.amazon.com/lambda/latest/api/API_SnapStartResponse.html) 为 `On` 且 [State](https://docs.aws.amazon.com/lambda/latest/api/API_GetFunctionConfiguration.html#lambda-GetFunctionConfiguration-response-State)（状态）为 `Active`，则表示 SnapStart 已激活，并且已为指定函数版本生成快照。

   ```
   "SnapStart": { 
           "ApplyOn": "PublishedVersions",
           "OptimizationStatus": "On"
        },
        "State": "Active",
   ```

1. 使用 [invoke](https://docs.aws.amazon.com/lambda/latest/api/API_Invoke.html) 操作调用相应函数版本。

## Lambda SnapStart 和函数状态
<a name="snapstart-function-states"></a>

使用 SnapStart 时可能会出现以下函数状态。

**待处理**  
Lambda 正在初始化您的代码并为已初始化的执行环境创建快照。在该函数版本上运行的任何调用或其他 API 操作都将失败。

**活动**  
快照创建已完成，您可以调用此函数。若要使用 SnapStart，您必须调用已发布的函数版本，而不是未发布的版本（\$1LATEST）。

**非活动**  
当 Lambda 定期重新生成函数快照以应用软件更新时，就会出现 `Inactive` 状态。在这种情况下，如果您的函数无法初始化，则该函数可能进入 `Inactive` 状态。  
对于使用 Java 运行时的函数，Lambda 会在 14 天未调用后删除快照。如果您在 14 天后调用函数版本，Lambda 会返回 `SnapStartNotReadyException` 响应并开始初始化新的快照。函数版本达到 `Active` 状态之后再调用该函数。

**失败**  
Lambda 在运行初始化代码或创建快照时出现错误。

## 更新快照
<a name="update-snapshot"></a>

Lambda 为每个已发布的函数版本创建快照。若要更新快照，请发布新的函数版本。

## 将 SnapStart 与 AWS SDK 结合使用
<a name="snapstart-credentials"></a>

为了从您的函数调用 AWS 开发工具包，Lambda 通过代入函数的执行角色来生成一组临时凭证。这些凭证在函数调用期间可用作环境变量。您无需直接在代码中为开发工具包提供凭证。默认情况下，凭证提供程序链会按顺序检查每个可以设置凭证的位置，然后选择第一个可用位置，通常是环境变量（`AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY` 和 `AWS_SESSION_TOKEN`）。

**注意**  
SnapStart 激活后，Lambda 运行时会自动使用容器凭证（`AWS_CONTAINER_CREDENTIALS_FULL_URI` 和 `AWS_CONTAINER_AUTHORIZATION_TOKEN`），而非访问密钥环境变量。这样可以防止凭证在函数还原之前过期。

## 将 SnapStart 与 CloudFormation、AWS SAM 和 AWS CDK 结合使用
<a name="snapstart-cfn-sam"></a>
+ **AWS CloudFormation：**在您的模板中声明 [SnapStart](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) 实体。
+ **AWS Serverless Application Model (AWS SAM)：**在您的模板中声明 [SnapStart](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html#sam-function-snapstart) 属性。
+ **AWS Cloud Development Kit (AWS CDK)：**使用 [SnapStartProperty](https://docs.aws.amazon.com/cdk/api/v2/java/software/amazon/awscdk/services/lambda/CfnFunction.SnapStartProperty.html) 类型。

## 删除快照
<a name="snapshot-delete"></a>

在以下情况下 Lambda 会删除快照：
+ 您删除函数或函数版本。
+ **仅限 Java 运行时**：您在 14 天内未调用该函数版本。14 天未被调用后，函数版本将转换为[非活动](#snapstart-function-states)状态。如果您在 14 天后调用函数版本，Lambda 会返回 `SnapStartNotReadyException` 响应并开始初始化新的快照。函数版本达到[活动](#snapstart-function-states)状态之后再调用该函数。

Lambda 根据《一般数据保护条例》（GDPR）删除与已删除的快照相关的所有资源。

# 使用 Lambda SnapStart 处理唯一性
<a name="snapstart-uniqueness"></a>

调用在 SnapStart 函数上纵向扩展时，Lambda 使用单个初始化的快照来恢复多个执行环境。如果您的初始化代码生成了快照中包含的唯一内容，则跨执行环境重复使用该内容时，该内容可能不是唯一的。若要在使用 SnapStart 时保持唯一性，则必须在初始化后生成唯一的内容。这包括唯一的 ID、唯一的密钥和用于生成伪随机性的熵。

我们推荐使用以下最佳实践来帮助您保持代码的唯一性。对于 Java 函数，Lambda 还提供了开源 [SnapStart 扫描工具](#snapstart-scanning)，以帮助检查假设唯一性的代码。如果您在初始化阶段生成唯一的数据，则可以使用[运行时挂钩](snapstart-runtime-hooks.md)来还原唯一性。使用运行时挂钩，您可以在 Lambda 拍摄快照之前立即运行特定代码，也可以在 Lambda 从快照恢复函数后立即运行特定代码。

## 避免在初始化期间保存依赖唯一性的状态
<a name="snapstart-caching-unique"></a>

在函数的[初始化阶段](lambda-runtime-environment.md#runtimes-lifecycle-ib)，避免缓存旨在保持唯一性的数据，例如生成用于日志记录的唯一 ID 或为随机函数设置种子。相反，建议您在函数处理程序中生成唯一的数据或为随机函数设置种子 – 或使用[运行时钩子](snapstart-runtime-hooks.md)。

以下示例演示如何在函数处理程序中生成 UUID。

------
#### [ Java ]

**Example – 在函数处理程序中生成唯一的 ID**  

```
import java.util.UUID;
  public class Handler implements RequestHandler<String, String> {
    private static UUID uniqueSandboxId = null;
    @Override
    public String handleRequest(String event, Context context) {
      if (uniqueSandboxId == null)
        uniqueSandboxId = UUID.randomUUID();
      System.out.println("Unique Sandbox Id: " + uniqueSandboxId);
      return "Hello, World!";
    }
  }
```

------
#### [ Python ]

**Example – 在函数处理程序中生成唯一的 ID**  

```
import json
import random
import time

unique_number = None

def lambda_handler(event, context):
    seed = int(time.time() * 1000) 
    random.seed(seed)
    global unique_number
    if not unique_number:
        unique_number = random.randint(1, 10000)
        
    print("Unique number: ", unique_number)
    
    return "Hello, World!"
```

------
#### [ .NET ]

**Example – 在函数处理程序中生成唯一的 ID**  

```
namespace Example;
public class SnapstartExample
{
    private Guid _myExecutionEnvironmentGuid;
    public SnapstartExample()
    {
        // This GUID is set for non-restore use cases, such as testing or if SnapStart is turned off
        _myExecutionEnvironmentGuid = new Guid();
        // Register the method which will run after each restore. You may need to update Amazon.Lambda.Core to see this
        Amazon.Lambda.Core.SnapshotRestore.RegisterAfterRestore(MyAfterRestore);
    }

    private ValueTask MyAfterRestore()
    {
        // After restoring this snapshot to a new execution environment, update the GUID
        _myExecutionEnvironmentGuid = new Guid();
        return ValueTask.CompletedTask;
    }

    public string Handler()
    {
        return $"Hello World! My Execution Environment GUID is {_myExecutionEnvironmentGuid}";
    }
}
```

------

## 使用加密安全的伪随机数生成器（CSPRNG）
<a name="snapstart-csprng"></a>

如果您的应用程序依赖随机性，我们建议您使用加密安全的随机数生成器（CSPRNG）。除了 OpenSSL 1.0.2 之外，Lambda 托管式运行时还包括以下内置 CSPRNG：
+ **Java：**`java.security.SecureRandom`
+ **Python：**`random.SystemRandom`
+ **.NET：**`System.Security.Cryptography.RandomNumberGenerator`

始终从 `/dev/random` 或 `/dev/urandom` 获取随机数的软件也通过 SnapStart 保持随机性。

AWS 加密库会自动保持 SnapStart 的随机性，从下表中指定的最低版本开始。如果您将这些库与 Lambda 函数一起使用，请确保使用以下最低版本或更高版本：


****  

| 图书馆 | 支持的最低版本（x86） | 支持的最低版本（ARM） | 
| --- | --- | --- | 
| AWS libcrypto（AWS-LC） |  1.16.0  |  1.30.0  | 
| AWS libcrypto FIPS |  2.0.13  | 2.0.13 | 

如果通过以下库将上述加密库与 Lambda 函数打包为可传递依赖项，请确保使用以下最低版本或更高版本：


****  

| 图书馆 | 支持的最低版本（x86） | 支持的最低版本（ARM） | 
| --- | --- | --- | 
| AWS SDK for Java 2.x |  2.23.20  |  2.26.12  | 
| AWS Common Runtime for Java |  0.29.8  |  0.29.25  | 
| Amazon Corretto 加密提供商 |  2.4.1  | 2.4.1 | 
| Amazon Corretto 加密提供商 FIPS |  2.4.1  | 2.4.1 | 

以下示例演示如何使用 CSPRNG 来保证唯一的编号序列，即使从快照中还原函数也是如此。

------
#### [ Java ]

**Example – java.security.SecureRandom**  

```
import java.security.SecureRandom;
  public class Handler implements RequestHandler<String, String> {
    private static SecureRandom rng = new SecureRandom();
    @Override
    public String handleRequest(String event, Context context) {
      for (int i = 0; i < 10; i++) {
        System.out.println(rng.next());
      }
      return "Hello, World!";
    }
  }
```

------
#### [ Python ]

**Example – random.SystemRandom**  

```
import json
import random

secure_rng = random.SystemRandom()

def lambda_handler(event, context):
    random_numbers = [secure_rng.random() for _ in range(10)]
    
    for number in random_numbers:
        print(number)
    
    return "Hello, World!"
```

------
#### [ .NET ]

**Example – RandomNumberGenerator**  

```
using Amazon.Lambda.Core;
using System.Security.Cryptography;
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace DotnetSecureRandom;

public class Function
{
    public string FunctionHandler()
    {
        using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
        {
            byte[] randomUnsignedInteger32Bytes = new byte[4];
            for (int i = 0; i < 10; i++)
            {
                rng.GetBytes(randomUnsignedInteger32Bytes);
                int randomInt32 = BitConverter.ToInt32(randomUnsignedInteger32Bytes, 0);
                Console.WriteLine("{0:G}", randomInt32);
            }
        }
        return "Hello World!";
    }
}
```

------

## SnapStart 扫描工具（仅 Java）
<a name="snapstart-scanning"></a>

Lambda 为 Java 提供了扫描工具，以帮助您检查假设唯一性的代码。SnapStart 扫描工具是一个开源 [SpotBugs](https://spotbugs.github.io/) 插件，该工具根据一组规则运行静态分析。该扫描工具有助于识别可能打破有关唯一性假设的潜在代码实施。有关安装说明和扫描工具执行的检查列表，请参阅 GitHub 上的 [aws-lambda-snapstart-java-rules](https://github.com/aws/aws-lambda-snapstart-java-rules) 存储库。

要详细了解如何使用 SnapStart 处理唯一性，请参阅 AWS 计算博客上的[通过 AWS Lambda SnapStart 加快启动速度](https://aws.amazon.com/blogs/compute/starting-up-faster-with-aws-lambda-snapstart/)。

# 在 Lambda 函数快照之前或之后实现代码
<a name="snapstart-runtime-hooks"></a>

在 Lambda 创建快照之前或在 Lambda 从快照恢复函数之后，使用运行时钩子实施代码。运行时钩子对于许多用途非常有用，例如：
+ **清理和初始化：**在创建快照之前，您可以使用运行时钩子来执行清理或资源释放操作。还原快照后，您可以使用运行时钩子重新初始化未在快照中捕获的任何资源或状态。
+ **动态配置：**在创建快照之前或还原快照之后，您可以使用运行时钩子，以动态更新配置或其他元数据。如果您的函数需要适应运行时环境的变化，则这可能非常有用。
+ **外部集成：**作为检查点和还原过程的一部分，您可以使用运行时钩子与外部服务或系统集成，例如发送通知或更新外部状态。
+ **性能调优：**您可以使用运行时钩子来微调函数的启动顺序，例如通过预加载依赖项。有关更多信息，请参阅 [性能优化](snapstart-best-practices.md#snapstart-tuning)。

以下页面说明了如何为您的首选运行时实施运行时钩子。

**Topics**
+ [Java](snapstart-runtime-hooks-java.md)
+ [Python](snapstart-runtime-hooks-python.md)
+ [.NET](snapstart-runtime-hooks-dotnet.md)

# 适用于 Java 的 Lambda SnapStart 运行时钩子
<a name="snapstart-runtime-hooks-java"></a>

在 Lambda 创建快照之前或在 Lambda 从快照恢复函数之后，使用运行时钩子实施代码。运行时挂钩作为开源检查点协调还原（CRaC）项目的一部分提供。CRaC 正在开发[开放 Java 开发工具包（OpenJDK）](https://wiki.openjdk.org/display/crac)。有关如何将 CRaC 与参考应用程序结合使用的示例，请参阅 GitHub 上的 [CRaC](https://github.com/CRaC/docs/blob/master/STEP-BY-STEP.md) 存储库。CRaC 使用三个主要元素：
+ `Resource` – 具有两种方法的接口，`beforeCheckpoint()` 和 `afterRestore()`。使用这些方法实施要在快照之前和还原之后运行的代码。
+ `Context <R extends Resource>` – 若要接收检查点和还原的通知，`Resource` 必须注册到 `Context`。
+ `Core` – 协调服务，该服务通过静态方法 `Core.getGlobalContext()` 提供默认全局 `Context`。

有关 `Context` 和 `Resource` 的更多信息，请参阅 CRaC 文档中的 [Package org.crac](https://javadoc.io/doc/io.github.crac/org-crac/latest/index.html)（程序包 org.crac）。

使用以下步骤通过 [org.crac 程序包](https://github.com/CRaC/org.crac)实施运行时挂钩。Lambda 运行时包含自定义的 CRaC 上下文实施现，该实施会在检查点检查之前和还原后调用运行时挂钩。

## 运行时钩子注册和执行
<a name="runtime-hooks-registration-java"></a>

Lambda 执行运行时钩子的顺序由注册顺序决定。注册顺序遵循代码中导入、定义或执行的顺序。
+ `beforeCheckpoint()`：按注册的相反顺序执行
+ `afterRestore()`：按注册的顺序执行

请确保所有注册的钩子都已正确导入，并包含在函数代码中。如果在单独的文件或模块中注册运行时钩子，则您必须确保将该模块直接导入或作为较大程序包的一部分导入到函数的处理程序文件中。如果未在函数处理程序中导入文件或模块，则 Lambda 会忽略运行时钩子。

**注意**  
Lambda 创建快照时，初始化代码最多可以运行 15 分钟。时间限制为 130 秒或[配置的函数超时](configuration-timeout.md)（最大 900 秒），以较高者为准。您的 `beforeCheckpoint()` 运行时挂钩计入初始化代码时限。Lambda 还原快照时，运行时必须加载，并且 `afterRestore()` 运行时钩子必须在超时限制（10 秒）内完成。否则，您将收到 SnapStartTimeoutException。

## 步骤 1：更新构建配置
<a name="runtime-hooks-java-update-build"></a>

将 `org.crac` 依赖项添加到构建配置中。下面的示例使用了 Gradle。有关其他构建系统的示例，请参阅 [Apache Maven 文档](https://search.maven.org/artifact/io.github.crac/org-crac/0.1.3/jar)。

```
dependencies {
    compile group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.1'
    # All other project dependecies go here:
    # ...
    # Then, add the org.crac dependency:
 implementation group: 'org.crac', name: 'crac', version: '1.4.0'
}
```

## 第 2 步：更新 Lambda 处理程序
<a name="runtime-hooks-java-update-handler"></a>

Lambda 函数*处理程序*是函数代码中处理事件的方法。当调用函数时，Lambda 运行处理程序方法。您的函数会一直运行，直到处理程序返回响应、退出或超时。

有关更多信息，请参阅 [定义采用 Java 的 Lambda 函数处理程序](java-handler.md)。

以下示例处理程序介绍如何在检查点检查（`beforeCheckpoint()`）之前和还原（`afterRestore()`）之后运行代码。该处理程序还将 `Resource` 注册到运行时管理的全局 `Context` 中。

**注意**  
Lambda 创建快照时，初始化代码最多可以运行 15 分钟。时间限制为 130 秒或[配置的函数超时](configuration-timeout.md)（最大 900 秒），以较高者为准。您的 `beforeCheckpoint()` 运行时挂钩计入初始化代码时限。Lambda 还原快照时，必须加载运行时（JVM），并且 `afterRestore()` 运行时挂钩必须在超时限制（10 秒）内完成。否则，您将收到 SnapStartTimeoutException。

```
...
  import org.crac.Resource;
  import org.crac.Core;
  ... 
public class CRaCDemo implements RequestStreamHandler, Resource {
    public CRaCDemo() {
      Core.getGlobalContext().register(this);
    }
    public String handleRequest(String name, Context context) throws IOException {
      System.out.println("Handler execution");
      return "Hello " + name;
    }
    @Override
    public void beforeCheckpoint(org.crac.Context<? extends Resource> context)
        throws Exception {
      System.out.println("Before checkpoint");
    }
    @Override
    public void afterRestore(org.crac.Context<? extends Resource> context)
        throws Exception {
      System.out.println("After restore");
```

`Context` 仅对注册对象保持 [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ref/WeakReference.html](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ref/WeakReference.html)。如果 [https://javadoc.io/static/io.github.crac/org-crac/0.1.3/org/crac/Resource.html](https://javadoc.io/static/io.github.crac/org-crac/0.1.3/org/crac/Resource.html) 是垃圾回收，则不会运行运行时挂钩。您的代码必须保持对 `Resource` 的强引用，以保证运行时挂钩正常运行。

以下是两个应避免的模式示例：

**Example – 没有强引用的对象**  

```
Core.getGlobalContext().register( new MyResource() );
```

**Example – 匿名类的对象**  

```
Core.getGlobalContext().register( new Resource() {
   
   @Override
   public void afterRestore(Context<? extends Resource> context) throws Exception {
    // ...
   }
   
   @Override
   public void beforeCheckpoint(Context<? extends Resource> context) throws Exception {
    // ...
   }

} );
```

相反，保持强引用。在以下示例中，注册的资源不是垃圾回收的，运行时挂钩可以持续运行。

**Example – 具有强引用的对象**  

```
Resource myResource = new MyResource(); // This reference must be maintained to prevent the registered resource from being garbage collected
Core.getGlobalContext().register( myResource );
```

# 适用于 Python 的 Lambda SnapStart 运行时钩子
<a name="snapstart-runtime-hooks-python"></a>

在 Lambda 创建快照之前或在 Lambda 从快照恢复函数之后，使用运行时钩子实施代码。Python 运行时钩子作为开源 [Python 库的快照还原](https://pypi.org/project/snapshot-restore-py/)（包含在 Python 托管式运行时中）的一部分提供。此库提供了两种可用于定义运行时钩子的修饰器：
+ `@register_before_snapshot`：适用于要在 Lambda 创建快照之前运行的函数。
+ `@register_after_restore`：适用于要在 Lambda 从快照恢复函数时运行的函数。

或者，您可以使用以下方法，注册运行时钩子的可调用对象：
+ `register_before_snapshot(func, *args, **kwargs)`
+ `register_after_restore(func, *args, **kwargs)`

## 运行时钩子注册和执行
<a name="runtime-hooks-registration-python"></a>

Lambda 执行运行时钩子的顺序由注册顺序决定：
+ 快照前：按注册的相反顺序执行
+ 快照后：按注册的顺序执行

运行时钩子注册的顺序取决于定义钩子的方式。使用装饰器（`@register_before_snapshot` 和 `@register_after_restore`）时，注册顺序遵循代码中导入、定义或执行的顺序。如果您需要对注册顺序有更多控制，则请使用 `register_before_snapshot()` 和 `register_after_restore()` 方法代替装饰器。

请确保所有注册的钩子都已正确导入，并包含在函数代码中。如果在单独的文件或模块中注册运行时钩子，则您必须确保将该模块直接导入或作为较大程序包的一部分导入到函数的处理程序文件中。如果未在函数处理程序中导入文件或模块，则 Lambda 会忽略运行时钩子。

**注意**  
Lambda 创建快照时，初始化代码最多可以运行 15 分钟。时间限制为 130 秒或[配置的函数超时](configuration-timeout.md)（最大 900 秒），以较高者为准。您的 `@register_before_snapshot` 运行时挂钩计入初始化代码时限。Lambda 还原快照时，运行时必须加载，并且 `@register_after_restore` 运行时钩子必须在超时限制（10 秒）内完成。否则，您将收到 SnapStartTimeoutException。

## 示例
<a name="runtime-hooks-python-code-sample"></a>

以下示例处理程序介绍如何在检查点检查（`@register_before_snapshot`）之前和还原（`@register_after_restore`）之后运行代码。

```
from snapshot_restore_py import register_before_snapshot, register_after_restore

def lambda_handler(event, context):
    # Handler code

@register_before_snapshot
def before_checkpoint():
    # Logic to be executed before taking snapshots

@register_after_restore
def after_restore():
    # Logic to be executed after restore
```

有关更多示例，请参阅 AWS GitHub 存储库中的 [Snapshot Restore for Python](https://github.com/aws/snapshot-restore-py/tree/main/examples)。

# 适用于 .NET 的 Lambda SnapStart 运行时钩子
<a name="snapstart-runtime-hooks-dotnet"></a>

在 Lambda 创建快照之前或在 Lambda 从快照恢复函数之后，使用运行时钩子实施代码。.NET 运行时钩子作为 [Amazon.Lambda.Core](https://www.nuget.org/packages/Amazon.Lambda.Core) 程序包（2.5.0 版或更高版本）的一部分提供。此库提供了两种可用于定义运行时钩子的方法：
+ `RegisterBeforeSnapshot()`：创建快照之前运行的代码
+ `RegisterAfterSnapshot()`：从快照恢复函数之后运行的代码

**注意**  
如果您使用的是[适用于.NET 的 Lambda 注释框架](csharp-handler.md#csharp-handler-annotations)，则请升级到 [Amazon.Lambda.Annotations](https://www.nuget.org/packages/Amazon.Lambda.Annotations) 1.6.0 版或更高版本，以确保与 SnapStart 兼容。

## 运行时钩子注册和执行
<a name="runtime-hooks-registration-dotnet"></a>

在初始化代码中注册钩子。根据 Lambda 函数的[执行模型](csharp-handler.md#csharp-handler-setup)，请考虑以下指南：
+ 对于[可执行程序集方法](csharp-handler.md#csharp-executable-assembly-handlers)，请在使用 `RunAsync` 启动 Lambda 引导程序之前注册钩子。
+ 对于[类库方法](csharp-handler.md#csharp-class-library-handlers)，请在处理程序类构造函数中注册钩子。
+ 对于 [ASP.NET Core 应用程序](csharp-package-asp.md)，请在调用 `WebApplications.Run` 方法之前注册钩子。

要在 .NET 中注册 SnapStart 的运行时钩子，请使用以下方法：

```
Amazon.Lambda.Core.SnapshotRestore.RegisterBeforeSnapshot(BeforeCheckpoint);
Amazon.Lambda.Core.SnapshotRestore.RegisterAfterRestore(AfterCheckpoint);
```

注册多个钩子类型时，Lambda 执行运行时钩子的顺序由注册顺序决定：
+ `RegisterBeforeSnapshot()`：按注册的相反顺序执行
+ `RegisterAfterSnapshot()`：按注册的顺序执行

**注意**  
Lambda 创建快照时，初始化代码最多可以运行 15 分钟。时间限制为 130 秒或[配置的函数超时](configuration-timeout.md)（最大 900 秒），以较高者为准。您的 `RegisterBeforeSnapshot()` 运行时挂钩计入初始化代码时限。Lambda 还原快照时，运行时必须加载，并且 `RegisterAfterSnapshot()` 运行时钩子必须在超时限制（10 秒）内完成。否则，您将收到 SnapStartTimeoutException。

## 示例
<a name="runtime-hooks-dotnet-code-sample"></a>

以下示例函数介绍如何在检查点检查（`RegisterBeforeSnapshot`）之前和还原（`RegisterAfterRestore`）之后运行代码。

```
public class SampleClass
{
    public SampleClass()
    {
        Amazon.Lambda.Core.SnapshotRestore.RegisterBeforeSnapshot(BeforeCheckpoint);
        Amazon.Lambda.Core.SnapshotRestore.RegisterAfterRestore(AfterCheckpoint);
    }

    private ValueTask BeforeCheckpoint()
    {
        // Add logic to be executed before taking the snapshot
        return ValueTask.CompletedTask;
    }

    private ValueTask AfterCheckpoint()
    {
        // Add logic to be executed after restoring the snapshot
        return ValueTask.CompletedTask;
    }

    public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
    {
        // Add business logic

        return new APIGatewayProxyResponse
        {
            StatusCode = 200
        };
    }
}
```

# 监控 Lambda SnapStart
<a name="snapstart-monitoring"></a>

您可以使用 Amazon CloudWatch、AWS X-Ray 和 [使用遥测 API 访问扩展的实时遥测数据](telemetry-api.md) 监控 Lambda SnapStart 函数。

**注意**  
`AWS_LAMBDA_LOG_GROUP_NAME` 和 `AWS_LAMBDA_LOG_STREAM_NAME` [环境变量](configuration-envvars.md#configuration-envvars-runtime)在 Lambda SnapStart 函数中不可用。

## 了解使用 SnapStart 的日志记录和计费行为
<a name="snapstart-cloudwatch"></a>

SnapStart 函数的 [CloudWatch 日志流](monitoring-cloudwatchlogs.md)格式存在一些差异：
+ **初始化日志** – 创建新的执行环境时，`REPORT` 不会包含 `Init Duration` 字段。这是因为 Lambda 在您创建版本时而不是在函数调用期间初始化 SnapStart 函数。对于 SnapStart 函数，`Init Duration` 字段在 `INIT_REPORT` 记录中。此记录显示 [Init 阶段](lambda-runtime-environment.md#runtimes-lifecycle-ib) 的持续时间详细信息，包括任何 `beforeCheckpoint` [运行时挂钩](snapstart-runtime-hooks.md)的持续时间。
+ **调用日志** – 创建新的执行环境时，`REPORT` 会包括 `Restore Duration` 和 `Billed Restore Duration` 字段：
  + `Restore Duration`：Lambda 还原快照、加载运行时和运行任何还原后[运行时挂钩](snapstart-runtime-hooks.md)所花费的时间。恢复快照的过程可能包含在 MicroVM 之外的活动上花费的时间。`Restore Duration` 中报告了此时间。
  + `Billed Restore Duration`：Lambda 加载运行时和运行任何还原后[运行时挂钩](snapstart-runtime-hooks.md)所花费的时间。

**注意**  
与所有 Lambda 函数一样，持续时间收费适用于在函数处理程序中运行的代码。对于 SnapStart 函数，持续时间费用也适用于在处理程序之外声明的初始化代码、运行时加载所需的时间，以及在[运行时钩子](snapstart-runtime-hooks.md)中运行的任何代码。

冷启动持续时间为 `Restore Duration` \$1 `Duration` 的总和。

以下示例为 Lambda Insights 查询，该查询返回 SnapStart 函数的延迟百分位数。有关 Lambda Insights 查询的更多信息，请参阅 [使用查询排除函数故障的示例工作流程](monitoring-insights.md#monitoring-insights-queries)。

```
filter @type = "REPORT"
  | parse @log /\d+:\/aws\/lambda\/(?<function>.*)/
  | parse @message /Restore Duration: (?<restoreDuration>.*?) ms/
  | stats
count(*) as invocations,
pct(@duration+coalesce(@initDuration,0)+coalesce(restoreDuration,0), 50) as p50,
pct(@duration+coalesce(@initDuration,0)+coalesce(restoreDuration,0), 90) as p90,
pct(@duration+coalesce(@initDuration,0)+coalesce(restoreDuration,0), 99) as p99,
pct(@duration+coalesce(@initDuration,0)+coalesce(restoreDuration,0), 99.9) as p99.9
group by function, (ispresent(@initDuration) or ispresent(restoreDuration)) as coldstart
  | sort by coldstart desc
```

## 适用于 SnapStart 的 X-Ray 活动跟踪
<a name="snapstart-xray"></a>

您可以使用 [X-Ray](services-xray.md) 来跟踪向 Lambda SnapStart 函数发送的请求。SnapStart 函数的 X-Ray 子分段存在一些差异：
+ SnapStart 函数没有 `Initialization` 子分段。
+ `Restore` 子分段会显示 Lambda 还原快照、加载运行时和运行任何还原后[运行时挂钩](snapstart-runtime-hooks.md)所花费的时间。恢复快照的过程可能包含在 MicroVM 之外的活动上花费的时间。该时间在 `Restore` 子分段中报告。您无需为在 microVM 之外还原快照所花费的时间付费。

## SnapStart 的遥测 API 事件
<a name="snapstart-telemetry"></a>

Lambda 将以下 SnapStart 事件发送到 [遥测 API](telemetry-api.md)：
+ [`platform.restoreStart`](telemetry-schema-reference.md#platform-restoreStart) – 显示 [`Restore` 阶段](lambda-runtime-environment.md#runtimes-lifecycle-restore)的开始时间。
+ [`platform.restoreRuntimeDone`](telemetry-schema-reference.md#platform-restoreRuntimeDone) – 显示 `Restore` 阶段是否成功。当运行时发送 `restore/next` 运行时 API 请求时，Lambda 会发送此消息。存在三种可能的状态：成功、失败和超时。
+ [`platform.restoreReport`](telemetry-schema-reference.md#platform-restoreReport) – 显示 `Restore` 阶段的持续时间以及该阶段已计费的毫秒数。

## Amazon API Gateway 和函数 URL 指标
<a name="snapstart-metrics"></a>

如果您[使用 API Gateway](services-apigateway.md) 创建 Web API，则可以使用 [IntegrationLatency](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-metrics-and-dimensions.html) 指标来测量端到端延迟（API Gateway 将请求转发到后端和从后端收到响应之间的时间）。

如果使用 [Lambda 函数 URL](urls-configuration.md)，则可以使用 [UrlRequestLatency](urls-monitoring.md) 指标来测量端到端延迟（函数 URL 收到请求和函数 URL 返回响应之间的时间）。

# Lambda SnapStart 的安全模型
<a name="snapstart-security"></a>

Lambda SnapStart 支持静态加密。Lambda 使用 AWS KMS key 加密快照。默认情况下，Lambda 使用 AWS 托管式密钥。如果此默认行为适合您的工作流，那么您无需设置任何其他内容。否则，您可以使用 [create-function](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html) 中的 `--kms-key-arn` 选项或 [update-function-configuration](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/update-function-configuration.html) 命令来提供 AWS KMS 客户管理型密钥。这样做可能是为了控制 KMS 密钥的轮换，或者是为了满足组织管理 KMS 密钥的要求。客户托管式密钥产生标准 AWS KMS 费用。有关更多信息，请参阅[AWS Key Management Service 定价](https://aws.amazon.com/kms/pricing/)。

删除 SnapStart 函数或函数版本时，对该函数或函数版本的所有 `Invoke` 请求将失败。Lambda 根据《一般数据保护条例》（GDPR）删除与已删除的快照相关的所有资源。

# 最大限度地提高 Lambda SnapStart 的性能
<a name="snapstart-best-practices"></a>

**Topics**
+ [性能优化](#snapstart-tuning)
+ [联网最佳实践](#snapstart-networking)

## 性能优化
<a name="snapstart-tuning"></a>

要最大限度地发挥 SnapStart 的优势，请考虑为运行时提供以下代码优化建议。

**注意**  
SnapStart 在与大规模函数调用搭配使用时效果最佳。不经常调用的函数可能无法获得相同的性能改进。

### Java
<a name="snapstart-tuning-java"></a>

为了最大限度地发挥 SnapStart 的优势，我们建议您在初始化代码中而不是在函数处理程序中预加载依赖项并初始化导致启动延迟的资源。这会将与大量类加载相关的延迟移出调用路径，从而优化了 SnapStart 的启动性能。

如果您在初始化期间无法预加载依赖项或资源，那么我们建议您使用虚拟调用进行预加载。为此，请更新函数处理程序代码，如 AWS Labs GitHub 存储库上[宠物商店函数](https://github.com/awslabs/aws-serverless-java-container/tree/main/samples/spring/pet-store)的以下示例所示。

```
private static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
  static {
      try {
          handler = SpringLambdaContainerHandler.getAwsProxyHandler(PetStoreSpringAppConfig.class);

          // Use the onStartup method of the handler to register the custom filter
          handler.onStartup(servletContext -> {
              FilterRegistration.Dynamic registration = servletContext.addFilter("CognitoIdentityFilter", CognitoIdentityFilter.class);
              registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
          });

          // Send a fake Amazon API Gateway request to the handler to load classes ahead of time
          ApiGatewayRequestIdentity identity = new ApiGatewayRequestIdentity();
          identity.setApiKey("foo");
          identity.setAccountId("foo");
          identity.setAccessKey("foo");

          AwsProxyRequestContext reqCtx = new AwsProxyRequestContext();
          reqCtx.setPath("/pets");
          reqCtx.setStage("default");
          reqCtx.setAuthorizer(null);
          reqCtx.setIdentity(identity);

          AwsProxyRequest req = new AwsProxyRequest();
          req.setHttpMethod("GET");
          req.setPath("/pets");
          req.setBody("");
          req.setRequestContext(reqCtx);

          Context ctx = new TestContext();
          handler.proxy(req, ctx);


      } catch (ContainerInitializationException e) {
          // if we fail here. We re-throw the exception to force another cold start
          e.printStackTrace();
          throw new RuntimeException("Could not initialize Spring framework", e);
      }
  }
```

### Python
<a name="snapstart-tuning-python"></a>

要最大限度地发挥 SnapStart 的优势，请在 Python 函数内专注于高效的代码组织和资源管理。作为一般指南，在[初始化阶段](lambda-runtime-environment.md#runtimes-lifecycle-ib)执行繁重的计算任务。此方法将耗时的操作移出调用路径，从而提高了整体函数性能。为了有效实施这一策略，建议采用以下最佳实践：
+ 导入函数处理程序之外的依赖项。
+ 创建处理程序之外的 `boto3` 实例。
+ 在调用处理程序之前，初始化静态资源或配置。
+ 考虑使用快照前的[运行时钩子](snapstart-runtime-hooks-python.md)来执行资源密集型任务，例如下载外部文件、预加载 Django 等框架或加载机器学习模型。

**Example – 优化 SnapStart 的 Python 函数**  

```
# Import all dependencies outside of Lambda handler
from snapshot_restore_py import register_before_snapshot
import boto3
import pandas
import pydantic

# Create S3 and SSM clients outside of Lambda handler
s3_client = boto3.client("s3")

# Register the function to be called before snapshot
@register_before_snapshot
def download_llm_models():
    # Download an object from S3 and save to tmp
    # This files will persist in this snapshot
    with open('/tmp/FILE_NAME', 'wb') as f:
        s3_client.download_fileobj('amzn-s3-demo-bucket', 'OBJECT_NAME', f)
    ...

def lambda_handler(event, context):
    ...
```

### .NET
<a name="snapstart-tuning-dotnet"></a>

要减少即时（JIT）编译和程序集加载时间，请考虑从 `RegisterBeforeCheckpoint` [运行时钩子](snapstart-runtime-hooks-dotnet.md)调用函数处理程序。由于 .NET 分层编译的工作方式，您可以通过多次调用处理程序来获得最佳结果，如以下示例所示。

**重要**  
请确保您的虚拟函数调用不会产生意外的副作用，例如启动业务事务。

**Example**  

```
public class Function
{
    public Function()
    {
        Amazon.Lambda.Core.SnapshotRestore.RegisterBeforeSnapshot(FunctionWarmup);
    }

    // Warmup method that calls the function handler before snapshot to warm up the .NET code and runtime.
    // This speeds up future cold starts after restoring from a snapshot.

    private async ValueTask FunctionWarmup()
    {
        var request = new APIGatewayProxyRequest
        {
            Path = "/heathcheck",
            HttpMethod = "GET"
        };

        for (var i = 0; i < 10; i++)
        {
            await FunctionHandler(request, null);
        }
    }

    public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
    {
        //
        // Process HTTP request
        // 

        var response = new APIGatewayProxyResponse
        {
            StatusCode = 200
        };
        
        return await Task.FromResult(response);
    }
}
```

## 联网最佳实践
<a name="snapstart-networking"></a>

当 Lambda 从快照恢复您的函数时，无法保证您的函数在初始化阶段建立的连接的状态。在大多数情况下，AWS 开发工具包建立的网络连接会自动恢复。对于其他连接，我们建议您遵循以下最佳实践。

**重新建立网络连接**  
函数从快照恢复时，请务必重新建立网络连接。我们建议您在函数处理程序中重新建立网络连接。或者，您可以使用还原后的[运行时钩子](snapstart-runtime-hooks.md)。

**不要使用主机名作为唯一的执行环境标识符**  
我们建议不要使用 `hostname` 将执行环境标识为应用程序中的唯一节点或容器。通过 SnapStart，可以使用单个快照作为多个执行环境的初始状态。`InetAddress.getLocalHost()`（Java）、`socket.gethostname()`（Python）和 `Dns.GetHostName()`（.NET）的所有执行环境都返回相同的 `hostname` 值。对于需要唯一执行环境标识或 `hostname` 值的应用程序，我们建议您在函数处理程序中生成唯一的 ID。或者，使用还原后的[运行时钩子](snapstart-runtime-hooks.md)生成唯一的 ID，然后使用该唯一 ID 作为执行环境的标识符。

**避免将连接绑定到固定源端口**  
我们建议您避免将网络连接绑定到固定源端口。函数从快照恢复时，会重新建立连接，绑定到固定源端口的网络连接可能会失败。

**避免使用 Java DNS 缓存**  
Lambda 函数已经缓存了 DNS 响应。如果您将另一个 DNS 缓存与 SnapStart 结合使用，则函数从快照恢复时可能会出现连接超时。

`java.util.logging.Logger` 类可以间接启用 JVM DNS 缓存。要覆盖默认设置，请在初始化 `logger` 之前将 [networkaddress.cache.ttl](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/InetAddress.html#inetaddress-caching-heading) 设置为 0。示例：

```
public class MyHandler {
  // first set TTL property
  static{
   java.security.Security.setProperty("networkaddress.cache.ttl" , "0");
  }
 // then instantiate logger
  var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class);
}
```

为了防止 Java 11 运行时出现 `UnknownHostException` 故障，建议将 `networkaddress.cache.negative.ttl` 设置为 0。在 Java 17 及更高版本的运行时中，不必执行此步骤。您可以使用 `AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0` 环境变量为 Lambda 函数设置此属性。

禁用 JVM DNS 缓存并不能禁用 Lambda 的托管式 DNS 缓存。

# 排查 Lambda 函数的 SnapStart 错误
<a name="snapstart-troubleshooting"></a>

本页内容旨在解决使用 Lambda SnapStart 时出现的常见问题，包括快照创建错误、超时错误和内部服务错误。

## SnapStartNotReadyException
<a name="snapstartnotreadyexception"></a>

**错误：**调用 Invoke20150331 操作时出现错误 (SnapStartNotReadyException)：Lambda 正在初始化函数。只要函数状态变为 ACTIVE，即可加以调用。

### 常见原因
<a name="snapstartnotreadyexception-cause"></a>

在尝试调用处于 `Inactive` [状态](snapstart-activate.md#snapstart-function-states)的函数版本时，即会发生此错误。如果 14 天未调用函数版本或者 Lambda 定期回收执行环境，函数版本将变为 `Inactive`。

### 解决方案
<a name="snapstartnotreadyexception-resolution"></a>

函数版本达到 `Active` 状态之后再调用该函数。

## SnapStartTimeoutException
<a name="snapstart-invocation-failure"></a>

**问题：**在尝试调用 SnapStart 函数版本时收到 `SnapStartTimeoutException`。

### 常见原因
<a name="snapstart-invocation-failure-cause"></a>

在[还原](lambda-runtime-environment.md#runtimes-lifecycle-restore)阶段，Lambda 会还原 Java 运行时并运行任何还原后的[运行时钩子](snapstart-runtime-hooks.md)。如果还原后的运行时钩子的运行时间超过 10 秒，则 `Restore` 阶段会超时，并且在尝试调用该函数时会出现错误。网络连接和凭证问题也可能导致 `Restore` 阶段超时。

### 解决方案
<a name="snapstart-invocation-failure-resolution"></a>

查看函数的 CloudWatch 日志，了解在[还原](lambda-runtime-environment.md#runtimes-lifecycle-restore)阶段发生的超时错误。请确保所有还原后的钩子在不到 10 秒的时间内完成。

**Example CloudWatch 日志**  

```
{ "cause": "Lambda couldn't restore the snapshot within the timeout limit. (Service: Lambda, Status Code: 408, Request ID: 11a222c3-410f-427c-ab22-931d6bcbf4f2)", "error": "Lambda.SnapStartTimeoutException"}
```

## 500 内部服务错误
<a name="snapstart-500-error"></a>

**错误：**Lambda 无法创建新快照，因为已达到并发快照创建上限。

### 常见原因
<a name="snapstart-500-error-cause"></a>

500 错误是 Lambda 服务自身的内部错误，而非函数或代码的问题。这些错误通常间歇性发生。

### 解决方案
<a name="snapstart-500-error-resolution"></a>

尝试再次发布该函数版本。

## 401 未经授权
<a name="snapstart-401-unauthorized"></a>

**错误：**会话令牌或标头密钥错误

### 常见原因
<a name="snapstart-401-unauthorized-cause"></a>

在 Lambda SnapStart 中使用 [AWS Systems Manager Parameter Store 和 AWS Secrets Manager 扩展](with-secrets-manager.md)时会发生此错误。

### 解决方案
<a name="snapstart-401-unauthorized-resolution"></a>

AWS Systems Manager Parameter Store 和 AWS Secrets Manager 与 SnapStart 不兼容。该扩展会在函数初始化期间生成用于与 AWS Secrets Manager 通信的凭证，但该凭证在与 SnapStart 一起使用时会导致凭证过期错误。

## UnknownHostException（Java）
<a name="snapstart-dns-caching"></a>

**错误：**无法执行 HTTP 请求：`abc.us-east-1.amazonaws.com` 的证书与任何主题备用名称都不匹配。

### 常见原因
<a name="snapstart-dns-caching-cause"></a>

Lambda 函数已经缓存了 DNS 响应。如果您将另一个 DNS 缓存与 SnapStart 结合使用，则函数从快照恢复时可能会出现连接超时。

### 解决方案
<a name="snapstart-dns-caching-resolution"></a>

为了防止 Java 11 运行时出现 `UnknownHostException` 故障，建议将 `networkaddress.cache.negative.ttl` 设置为 0。在 Java 17 及更高版本的运行时中，不必执行此步骤。您可以使用 `AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL=0` 环境变量为 Lambda 函数设置此属性。

## 快照创建失败
<a name="snapstart-creation-failure"></a>

**错误：**AWS Lambda 无法调用 SnapStart 函数。如果此错误仍然存在，请检查函数的 CloudWatch 日志，看是否存在初始化错误。

### 解决方案
<a name="snapstart-creation-failure-resolution"></a>

查看函数的 Amazon CloudWatch 日志，了解检查点前的[运行时钩子](snapstart-runtime-hooks.md)是否超时。有时候，发布新的函数版本也可以解决该问题。

## 快照创建延迟
<a name="snapstart-creation-latency"></a>

**问题：**在发布新的函数版本时，该函数会长时间保持 `Pending` [状态](snapstart-activate.md#snapstart-function-states)。

### 常见原因
<a name="snapstart-creation-latency-cause"></a>

Lambda 创建快照时，初始化代码最多可以运行 15 分钟。时间限制为 130 秒或[配置的函数超时](configuration-timeout.md)（最大 900 秒），以较高者为准。

如果函数[已附加到 VPC](configuration-vpc.md#configuration-vpc-attaching)，Lambda 可能还需要在函数变为 `Active` 之前创建网络接口。如果在函数处于 `Pending` 状态时尝试调用函数版本，可能会出现 409 `ResourceConflictException`。如果使用 Amazon API Gateway 端点调用该函数，API Gateway 中可能会出现 500 错误。

### 解决方案
<a name="snapstart-creation-latency-resolution"></a>

至少等待 15 分钟让函数版本初始化，然后再调用函数。