

# 在 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
        };
    }
}
```