

# 适用于 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 );
```