

# 使用 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/)。