28 个版本 (12 个重大变更)
0.13.0 | 2024 年 7 月 25 日 |
---|---|
0.11.3 | 2024 年 6 月 14 日 |
0.11.1 | 2024 年 3 月 30 日 |
0.9.0 | 2023 年 12 月 20 日 |
0.1.0 | 2018 年 11 月 29 日 |
#12 in HTTP 服务器
209,960 个月下载量
在 76 个 Crates 中使用 (47 直接使用)
130KB
2K SLoC
Rust 运行时 for AWS Lambda
此包使您能够轻松运行用 Rust 编写的 AWS Lambda 函数。此工作区包括多个 Crates
lambda-runtime
是一个提供 Rust 应用程序 Lambda 运行的库。lambda-http
是一个库,它使您能够在 Rust 中轻松编写 API Gateway 代理事件集中的 Lambda 函数。lambda-extension
是一个库,它使您能够在 Rust 中轻松编写 Lambda 运行时扩展。lambda-events
是一个库,它提供了 Rust 中的强类型 Lambda 事件结构体。lambda-runtime-api-client
是 lambda 运行时库和 lambda 扩展库之间的共享库,它包含了一个用于与 AWS Lambda 运行时 API 通信的通用 API 客户端。
Rust 运行时客户端是一个实验性包。它可能会更改,仅用于评估目的。
入门
使用 Rust 编写 Lambda 函数的最简单方法是使用相关的项目 Cargo Lambda。Cargo Lambda 是一个 Cargo 插件或子命令,它提供了一些命令来帮助您在 AWS Lambda 上使用 Rust。
安装 Cargo Lambda 的首选方法是使用软件包管理器。
1- 在 MacOS 上使用 Homebrew
brew tap cargo-lambda/cargo-lambda
brew install cargo-lambda
2- 在 Scoop 上使用 Windows
scoop bucket add cargo-lambda https://github.com/cargo-lambda/scoop-cargo-lambda
scoop install cargo-lambda/cargo-lambda
或在任何已安装Python 3的系统上使用PiP
pip3 install cargo-lambda
请参阅Cargo Lambda 文档中的其他安装选项。
您的第一个函数
要创建您的第一个函数,请使用子命令 new
运行 Cargo Lambda。此命令将为您的函数生成一个带有初始源代码的 Rust 包
cargo lambda new YOUR_FUNCTION_NAME
示例函数
如果您想手动创建第一个函数,下面的代码演示了一个简单的函数,该函数接收一个带有 firstName
字段的 event,并返回一个消息给调用者。
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Error> {
let func = service_fn(func);
lambda_runtime::run(func).await?;
Ok(())
}
async fn func(event: LambdaEvent<Value>) -> Result<Value, Error> {
let (event, _context) = event.into_parts();
let first_name = event["firstName"].as_str().unwrap_or("world");
Ok(json!({ "message": format!("Hello, {}!", first_name) }))
}
理解 Lambda 错误
当函数调用失败时,AWS Lambda 预期您返回一个可以序列化为 JSON 结构的对象,其中包含错误信息。此结构如下所示
{
"error_type": "the type of error raised",
"error_message": "a string description of the error"
}
Lambda 的 Rust 运行时使用名为 Diagnostic
的结构来在内部表示函数错误。运行时实现了将几个通用错误类型,如 std::error::Error
,转换为 Diagnostic
。对于这些通用实现,error_type
是您函数返回的值类型的名称。例如,如果您的函数返回 lambda_runtime::Error
,则 error_type
将类似于 alloc::boxed::Box<dyn core::error::Error + core::marker::Send + core::marker::Sync>
,这并不十分描述性。
实现您自己的 Diagnostic
为了获取更具描述性的 error_type
字段,您可以为您的错误类型实现 From
。这将使您完全控制 error_type
是什么
use lambda_runtime::{Diagnostic, Error, LambdaEvent};
#[derive(Debug)]
struct ErrorResponse(&'static str);
impl From<ErrorResponse> for Diagnostic {
fn from(error: ErrorResponse) -> Diagnostic {
Diagnostic {
error_type: "MyErrorType".into(),
error_message: error.0.to_string(),
}
}
}
async fn handler(_event: LambdaEvent<()>) -> Result<(), ErrorResponse> {
Err(ErrorResponse("this is an error response"))
}
我们建议您使用 thiserror crate 来声明您的错误。您可以在我们的 示例存储库 中查看如何将 thiserror
与运行时的诊断集成。
无论如何,Eyre 和 Miette
像 Anyhow、Eyre 和 Miette 这样的流行错误 crate 提供了它们自己的错误类型,这些类型封装了其他错误。这些错误没有直接转换为 Diagnostic
,但我们为每个这些 crate 提供了功能标志,以帮助您将它们与 Lambda 函数集成。
如果您在包的 lambda_runtime
依赖项中启用了 anyhow
、eyre
或 miette
功能。这些 crate 提供的错误类型可以具有通配转换到 Diagnostic
。这些功能公开了一个 From<T> for Diagnostic
实现,将那些错误类型转换为 Diagnostic
。以下是一个将 anyhow::Error
转换为 Diagnostic
的示例
use lambda_runtime::{Diagnostic, LambdaEvent};
async fn handler(_event: LambdaEvent<Request>) -> Result<(), Diagnostic> {
Err(anyhow::anyhow!("this is an error").into())
}
您可以在我们的示例仓库中找到更多如何使用这些错误包的示例。
构建和部署您的 Lambda 函数
如果您已经在您的机器上安装了 Cargo Lambda,请运行以下命令来构建您的函数
cargo lambda build --release
还有其他构建函数的方法:使用 AWS CLI 手动构建,使用AWS SAM,以及使用Serverless 框架。
4. 交叉编译您的 Lambda 函数
默认情况下,Cargo Lambda 将您的函数构建为在 x86_64 架构上运行。如果您想使用不同的架构,请使用以下描述的选项。
6.1. 构建 Lambda 函数
Amazon Linux 2023
我们建议您使用 Amazon Linux 2023(例如 provided.al2023
),因为它包含一个更新的 GLIBC 版本,许多 Rust 程序都依赖于它。要为 Amazon Linux 2023 运行时构建 Lambda 函数,请运行
cargo lambda build --release --arm64
9. 将二进制文件部署到 AWS Lambda
对于自定义运行时,AWS Lambda 会查找部署包 zip 中的名为 bootstrap
的可执行文件。将生成的可执行文件重命名为 bootstrap
并将其添加到 zip 存档中。
您可以在 target/lambda
目录下找到您函数的 bootstrap
二进制文件。
12.1. 使用 Cargo Lambda 部署
一旦您使用前面描述的任一选项构建了代码,请使用 deploy
子命令将您的函数上传到 AWS
cargo lambda deploy
注意 确保用您账户中现有的角色替换执行角色!
此命令将创建一个与您的 Rust 包同名的 Lambda 函数。您可以通过在命令末尾添加参数来更改函数的名称
cargo lambda deploy my-first-lambda-function
注意 请参阅Cargo Lambda 文档中的其他部署选项。
您可以使用 invoke 子命令测试该函数
cargo lambda invoke --remote \
--data-ascii '{"command": "hi"}' \
--output-format json \
my-first-lambda-function
注意 例子中的 CLI 命令使用 Linux/MacOS 语法。对于 Windows CMD 或 PowerShell 等不同外壳,当使用嵌套引号时,需要修改语法,如
'{"command": "hi"}'
。可能需要使用反斜杠进行转义。有关更多信息,请参阅AWS CLI 参考。
19. 使用 AWS CLI 部署
您还可以使用 AWS CLI 部署您的 Rust 函数。首先,您需要创建一个包含您函数的 ZIP 存档。如果添加了 output-format
标志,Cargo Lambda 可以在构建二进制文件时自动为您完成此操作
cargo lambda build --release --arm64 --output-format zip
您可以在 target/lambda/YOUR_PACKAGE/bootstrap.zip
中找到生成的 zip 文件。使用该文件路径使用 AWS CLI 部署您的函数
$ aws lambda create-function --function-name rustTest \
--handler bootstrap \
--zip-file fileb://./target/lambda/basic/bootstrap.zip \
--runtime provided.al2023 \ # Change this to provided.al2 if you would like to use Amazon Linux 2
--role arn:aws:iam::XXXXXXXXXXXXX:role/your_lambda_execution_role \
--environment Variables={RUST_BACKTRACE=1} \
--tracing-config Mode=Active
注意 确保用您账户中现有的角色替换执行角色!
现在您可以使用 AWS CLI 或 AWS Lambda 控制台测试该函数
$ aws lambda invoke
--cli-binary-format raw-in-base64-out \
--function-name rustTest \
--payload '{"command": "Say Hi!"}' \
output.json
$ cat output.json # Prints: {"msg": "Command Say Hi! executed."}
注意 当使用 AWS CLI 版本 2 时,
--cli-binary-format raw-in-base64-out
是一个必需的参数。更多信息
2.3. AWS 无服务器应用程序模型 (SAM)
您可以使用 Rust 构建的 Lambda 函数与 AWS 无服务器应用程序模型 (SAM)。要这样做,您需要安装 AWS SAM CLI,它将帮助您在 AWS 账户中打包和部署您的 Lambda 函数。
您需要创建一个包含您所需基础设施的 template.yaml
文件。以下是一个包含单个 Lambda 函数的示例
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
MemorySize: 128
Architectures: ["arm64"]
Handler: bootstrap
Runtime: provided.al2023
Timeout: 5
CodeUri: target/lambda/basic/
Outputs:
FunctionName:
Value: !Ref HelloWorldFunction
Description: Name of the Lambda function
然后您可以使用 AWS SAM CLI 将您的 Lambda 函数部署到 AWS。
sam deploy --guided
最后,sam
将输出实际的 Lambda 函数名称。您可以使用此名称来调用您的函数。
$ aws lambda invoke
--cli-binary-format raw-in-base64-out \
--function-name HelloWorldFunction-XXXXXXXX \ # Replace with the actual function name
--payload '{"command": "Say Hi!"}' \
output.json
$ cat output.json # Prints: {"msg": "Command Say Hi! executed."}
本地开发和测试
使用单元和集成测试测试您的代码
AWS Lambda 事件是从 JSON 对象反序列化得到的普通结构。如果您的函数处理程序使用标准运行时,您可以使用 serde
将您的文本固定值反序列化到结构中,并直接调用您的处理程序。
#[test]
fn test_my_lambda_handler() {
let input = serde_json::from_str("{\"command\": \"Say Hi!\"}").expect("failed to parse event");
let context = lambda_runtime::Context::default();
let event = lambda_runtime::LambdaEvent::new(input, context);
my_lambda_handler(event).await.expect("failed to handle event");
}
如果您使用 lambda_http
接收 HTTP 事件,您也可以从纯文本固定值创建 http_lambda::Request
结构。
#[test]
fn test_my_lambda_handler() {
let input = include_str!("apigw_proxy_request.json");
let request = lambda_http::request::from_str(input)
.expect("failed to create request");
let response = my_lambda_handler(request).await.expect("failed to handle request");
}
带有 Cargo Lambda 的本地开发服务器
Cargo Lambda 提供了一个本地服务器,该服务器模拟 AWS Lambda 控制平面。此服务器支持 Windows、Linux 和 MacOS。在您的 Lambda 项目根目录下,您可以使用以下子命令来编译您的函数并启动服务器。
cargo lambda watch
现在您可以使用 cargo lambda invoke
向您的函数发送请求。例如
cargo lambda invoke <lambda-function-name> --data-ascii '{ "command": "hi" }'
在 HTTP 函数(函数 URL、API Gateway 等)上运行此命令将需要您使用适当的方案。您可以在 这里 找到这些方案的一些示例。否则,您将遇到以下错误。
Error: serde_json::error::Error
× data did not match any variant of untagged enum LambdaRequest
一个更简单的替代方案是根据您定义的地址和端口 cURL 以下端点。例如
curl -v -X POST \
'http://127.0.0.1:9000/lambda-url/<lambda-function-name>/' \
-H 'content-type: application/json' \
-d '{ "command": "hi" }'
警告 不要删除
content-type
头部。这是必要的,用于指导函数如何反序列化请求体。
您可以在项目的 文档页面 上阅读有关如何使用 cargo lambda watch 和 cargo lambda invoke 的更多信息。
Lambda 调试代理
您可以使用特殊的 Lambda 调试代理(由 @rimutaka 维护的非 AWS 存储库)在本地运行和调试 Lambda。这是一个 Lambda 函数,它将传入的请求转发到一个 AWS SQS 队列,并从另一个队列读取响应。在您的开发计算机上运行的一个本地代理读取队列,在本地调用您的 Lambda 并发送回响应。这种方法允许在 AWS 工作流程中本地调试 Lambda 函数,同时不需要修改本地和 AWS 版本的 Lambda 处理程序代码。
跟踪和日志记录
Lambda 的 Rust 运行时与 跟踪 库集成,以提供跟踪和日志记录功能。
默认情况下,运行时会发出 tracing
事件,您可以通过 tracing-subscriber
收集这些事件。它还启用了一个名为 tracing
的功能,该功能通过合理的选项将日志信息发送到 AWS CloudWatch。请参考以下示例,了解如何启用默认订阅者
use lambda_runtime::{run, service_fn, tracing, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing::init_default_subscriber();
run(service_fn(|event| tracing::info!(?event))).await
}
订阅者使用 RUST_LOG
环境变量来确定函数的日志级别。如果已配置,它还会使用 Lambda 的高级日志控制。
默认情况下,发出事件的日志级别是 INFO
。要获取更多详细信息,包括原始负载的转储,请以 TRACE
级别进行日志记录。
AWS 事件对象
本项目包括 Lambda 事件结构定义,aws_lambda_events
。这个包可以用来提供强类型化的 Lambda 事件结构。您也可以创建自己的自定义事件对象及其相应的结构。
自定义事件对象
为了序列化和反序列化事件和响应,我们建议使用 serde
库。为了接收自定义事件,使用 Serde 的宏注解您的结构
use serde::{Serialize, Deserialize};
use serde_json::json;
use std::error::Error;
#[derive(Serialize, Deserialize)]
pub struct NewIceCreamEvent {
pub flavors: Vec<String>,
}
#[derive(Serialize, Deserialize)]
pub struct NewIceCreamResponse {
pub flavors_added_count: usize,
}
fn main() -> Result<(), Box<Error>> {
let flavors = json!({
"flavors": [
"Nocciola",
"抹茶",
"आम"
]
});
let event: NewIceCreamEvent = serde_json::from_value(flavors)?;
let response = NewIceCreamResponse {
flavors_added_count: event.flavors.len(),
};
serde_json::to_string(&response)?;
Ok(())
}
支持的 Rust 版本 (MSRV)
AWS Lambda Rust 运行时需要至少 Rust 1.70,并且不保证在低于该版本的编译器上构建。
安全
有关更多信息,请参阅 CONTRIBUTING。
许可证
本项目采用 Apache-2.0 许可证。
依赖关系
~7–17MB
~219K SLoC