

# 构建 AWS Lambda 的自定义运行时系统
<a name="runtimes-custom"></a>

您可以采用任何编程语言实施 AWS Lambda 运行时。运行时是一个程序，调用函数时，改程序将运行 Lambda 函数的处理程序方法。该运行时系统可包含在函数的部署包中，也可以分布在[层](chapter-layers.md)中。创建 Lambda 函数时，请选择[仅限操作系统的运行时系统](runtimes-provided.md)（`provided` 运行时系统系列）。

**注意**  
创建自定义运行时系统是一个高级用例。如果您想了解有关编译为本机二进制文件或使用第三方现成运行时系统的信息，请参阅 [何时使用 Lambda 仅限操作系统的运行时](runtimes-provided.md)。

有关自定义运行时系统部署过程的演练，请参阅 [教程：构建自定义运行时系统](runtimes-walkthrough.md)。

**Topics**
+ [要求](#runtimes-custom-build)
+ [在自定义运行时中实施响应流](#runtimes-custom-response-streaming)
+ [为 Lambda 托管实例构建自定义运行时](#runtimes-custom-managed-instances)

## 要求
<a name="runtimes-custom-build"></a>

自定义运行时系统必须完成某些初始化和处理任务。运行时系统会运行函数的设置代码、从环境变量读取处理程序名称，以及从 Lambda 运行时系统 API 读取调用事件。运行时会将事件数据传递到函数处理程序，并将来自处理程序的响应发布回 Lambda。

### 初始化任务
<a name="runtimes-custom-initialization"></a>

初始化任务将对[函数的每个实例](lambda-runtime-environment.md)运行一次以准备用于处理调用的环境。
+ **检索设置** – 读取环境变量以获取有关函数和环境的详细信息。
  + `_HANDLER` – 处理程序的位置（来自函数的配置）。标准格式为 `file.method`，其中 `file` 是没有表达式的文件的名称，`method` 是在文件中定义的方法或函数的名称。
  + `LAMBDA_TASK_ROOT` – 包含函数代码的目录。
  + `AWS_LAMBDA_RUNTIME_API` – 运行时 API 的主机和端口。

  有关可用变量的完整列表，请参阅 [定义运行时环境变量](configuration-envvars.md#configuration-envvars-runtime)。
+ **初始化函数** – 加载处理程序文件并运行它包含的任何全局或静态代码。函数应该创建静态资源（如开发工具包客户端和数据库连接）一次，然后将它们重复用于多个调用。
+ **处理错误** – 如果出现错误，请调用[初始化错误](runtimes-api.md#runtimes-api-initerror) API 并立即退出。

计入收费的执行时间和超时的初始化计数。当执行触发您的函数的新实例的初始化时，您可以在日志和 [AWS X-Ray 跟踪](services-xray.md)中看到初始化时间。

**Example 日志**  

```
REPORT RequestId: f8ac1208... Init Duration: 48.26 ms   Duration: 237.17 ms   Billed Duration: 300 ms   Memory Size: 128 MB   Max Memory Used: 26 MB
```

### 处理任务
<a name="runtimes-custom-processing"></a>

其运行时，运行时将使用 [Lambda 运行时界面](runtimes-api.md)来管理传入事件和报告错误。完成初始化任务后，运行时将在一个循环中处理传入事件。在运行时代码中，按顺序执行下面的步骤。
+ **获取事件** – 调用[下一个调用](runtimes-api.md#runtimes-api-next) API 来获取下一个事件。响应正文包含事件数据。响应标头包含请求 ID 和其他信息。
+ **传播跟踪标头** – 从 API 响应中的 `Lambda-Runtime-Trace-Id` 标头获取 X-Ray 跟踪标头。使用相同的值在本地设置 `_X_AMZN_TRACE_ID` 环境变量。X-Ray SDK 使用此值在服务之间连接追踪数据。
+ **创建上下文对象** – 使用来自 API 响应中的环境变量和标头的上下文信息创建一个对象。
+ **调用函数处理程序** – 将事件和上下文对象传递到处理程序。
+ **处理响应** – 调用[调用响应](runtimes-api.md#runtimes-api-response) API 以发布来自处理程序的响应。
+ **处理错误** – 如果出现错误，请调用[调用错误](runtimes-api.md#runtimes-api-invokeerror) API。
+ **清理** – 释放未使用的资源，将数据发送到其他服务，或在获取下一个事件之前执行其他任务。

### Entrypoint
<a name="runtimes-custom-bootstrap"></a>

自定义运行时的入口点是一个名为 `bootstrap` 的可执行文件。引导文件可以是运行时，也可以调用创建运行时的另一个文件。如果部署包的根目录不包含名为 `bootstrap` 的文件，则 Lambda 将在函数的层中查找该文件。如果 `bootstrap` 文件不存在或不是可执行文件，则函数将在调用后返回 `Runtime.InvalidEntrypoint` 错误。

以下是一个 `bootstrap` 文件示例，该文件使用捆绑版本的 Node.js 在名为 `runtime.js` 的单独文件中运行 JavaScript 运行时系统。

**Example bootstrap**  

```
#!/bin/sh
    cd $LAMBDA_TASK_ROOT
    ./node-v11.1.0-linux-x64/bin/node runtime.js
```

## 在自定义运行时中实施响应流
<a name="runtimes-custom-response-streaming"></a>

对于[响应流式处理函数](configuration-response-streaming.md)，`response` 和 `error` 端点的行为略经修改，允许运行时系统将部分响应流式传输到客户端并以区块形式返回负载。有关特定行为的更多信息，请参阅以下内容：
+ `/runtime/invocation/AwsRequestId/response` – 从运行时系统传播 `Content-Type` 标头以发送到客户端。Lambda 通过 HTTP/1.1 分块传输编码，以区块形式返回响应负载。要将响应流式传输到 Lambda，运行时系统必须：
  + 将 `Lambda-Runtime-Function-Response-Mode` HTTP 标头设置为 `streaming`。
  + 将 `Transfer-Encoding` 标头设置为 `chunked`。
  + 编写符合 HTTP/1.1 分块传输编码规范的响应。
  + 成功编写响应后，关闭底层连接。
+ `/runtime/invocation/AwsRequestId/error` – 运行时系统可以使用此端点向 Lambda 报告函数或运行时系统错误，Lambda 也接受 `Transfer-Encoding` 标头。只能在运行时开始发送调用响应之前调用此端点。
+ 使用 `/runtime/invocation/AwsRequestId/response` 中的错误后缀报告中游错误 – 要报告运行时系统开始编写调用响应后发生的错误，运行时系统可以选择附加名为 `Lambda-Runtime-Function-Error-Type` 和 `Lambda-Runtime-Function-Error-Body` 的 HTTP 尾随标头。Lambda 将此视为成功响应，并将运行时系统提供的错误元数据转发给客户端。
**注意**  
要附加尾随标头，运行时必须在 HTTP 请求的开头设置 `Trailer` 标头值。这是 HTTP/1.1 分块传输编码规范的要求。
  + `Lambda-Runtime-Function-Error-Type` – 运行时系统遇到的错误类型。此标头由字符串值组成。Lambda 接受任何字符串，但建议您使用 *<category.reason>* 格式。例如 `Runtime.APIKeyNotFound`。
  + `Lambda-Runtime-Function-Error-Body` – 有关错误的 Base64 编码信息。

## 为 Lambda 托管实例构建自定义运行时
<a name="runtimes-custom-managed-instances"></a>

Lambda 托管实例使用与 Lambda（默认）函数相同的运行时 API。但是，为了支持托管实例的并发执行模型，自定义运行时的实施方式存在关键差异。

### 并发请求处理
<a name="runtimes-custom-managed-instances-concurrency"></a>

为托管实例构建自定义运行时的主要区别在于支持并发调用。与 Lambda（默认）函数的运行时一次处理一个调用不同，托管实例可以在单个执行环境中同时处理多个调用。

您的自定义运行时必须：
+ **支持并发的 `/next` 请求**：运行时可以对[下一个调用](runtimes-api.md#runtimes-api-next) API 同时进行多次调用，但最多不超过 `AWS_LAMBDA_MAX_CONCURRENCY` 环境变量指定的限制。
+ **处理并发的 `/response` 请求**：多个调用可以同时对[调用响应 API](runtimes-api.md#runtimes-api-response) 进行调用。
+ **实现线程安全的请求处理**：确保通过妥善管理共享资源和状态来确保并发调用不会相互干扰。
+ **使用唯一的请求 ID**：使用 `Lambda-Runtime-Aws-Request-Id` 标头分别跟踪每次调用，以便将响应与相应的请求进行匹配。

### 实施模式
<a name="runtimes-custom-managed-instances-implementation"></a>

托管实例运行时的典型实施模式涉及到创建工作线程或进程来处理并发调用：

1. **读取并发限制**：在初始化时，读取 `AWS_LAMBDA_MAX_CONCURRENCY` 环境变量，以确定要支持的并发调用数量。

1. **创建工作线程池**：初始化等于并发限制的工作线程池（线程、进程或异步任务）。

1. **工作线程处理循环**：每个工作线程独立进行以下操作：
   + 调用 `/runtime/invocation/next` 以获取调用事件
   + 使用事件数据调用函数处理程序
   + 将响应发布到 `/runtime/invocation/AwsRequestId/response`
   + 重复循环

### 其他注意事项
<a name="runtimes-custom-managed-instances-considerations"></a>
+ **日志记录格式**：托管实例仅支持 JSON 日志格式。确保您的运行时能够遵循 `AWS_LAMBDA_LOG_FORMAT` 环境变量并且仅使用 JSON 格式。有关更多信息，请参阅 [配置 JSON 和纯文本日志格式](monitoring-cloudwatchlogs-logformat.md)。
+ **共享资源**：将 `/tmp` 目录之类的共享资源与并发调用结合使用时，一定要谨慎。实施适当的锁定机制以避免竞争条件。

有关 Lambda 托管实例执行环境的更多信息，请参阅[了解 Lambda 托管实例执行环境](lambda-managed-instances-execution-environment.md)。