重要
此处包含的文档指的是 Swift AWS Lambda Runtime v2(来自 main 分支的代码)。如果您正在为 runtime v1.x 开发,请查看此 readme。
本指南包含以下章节
许多现代系统都有客户端组件(如 iOS、macOS 或 watchOS 应用程序)以及与这些客户端交互的服务器组件。对于客户端应用程序开发人员来说,Serverless 函数通常是将应用程序扩展到云端的最简单、最有效方式。
Serverless 函数正日益成为在云端运行事件驱动型或其他临时计算任务的热门选择。它们为关键任务的微服务和数据密集型工作负载提供支持。在许多情况下,Serverless 函数使开发人员能够更轻松地扩展和控制计算成本,因为它们具有按需性质。
使用 Serverless 函数时,必须注意资源利用率,因为它直接影响系统成本。这正是 Swift 的优势所在!凭借其低内存占用、确定性的性能和快速启动时间,Swift 非常适合 Serverless 函数架构。
将这一点与 Swift 的开发者友好性、表达性和对安全性的强调相结合,我们得到了一个对所有技能水平的开发者都非常友好的、可扩展且具有成本效益的解决方案。
Swift AWS Lambda Runtime 旨在使使用 Swift 构建 Lambda 函数变得简单而安全。该库是 AWS Lambda Runtime API 的实现,并使用基于 SwiftNIO 的嵌入式异步 HTTP 客户端,该客户端针对 AWS Runtime 上下文中的性能进行了微调。该库提供了一个多层 API,允许构建各种 Lambda 函数:从快速简单的闭包到复杂的、对性能敏感的事件处理程序。
确保您已安装 Swift 6.x 工具链。您可以从 Swift.org 安装 Swift 工具链
在 macOS 上开发时,请确保您使用 macOS 15 (Sequoia) 或更高版本的 macOS。
要构建和归档您的 Lambda 函数,您需要安装 docker。
要部署 Lambda 函数并调用它,您必须拥有 一个 AWS 账户,并安装和配置 aws
命令行。
要开始使用,请阅读 Swift AWS Lambda runtime 教程。它为开发人员提供了详细的分步说明,以开发、构建和部署 Lambda 函数。
我们还编写了一份全面的部署指南。
或者,如果您迫不及待地想开始使用 runtime v2,请尝试以下六个步骤
Examples/_MyFirstFunction
包含一个脚本,该脚本将完成本节中描述的步骤。
如果您真的迫不及待,只需输入
cd Examples/_MyFirstFunction
./create_and_deploy_function.sh
否则,请继续阅读。
mkdir MyLambda && cd MyLambda
swift package init --type executable
准备您的 Package.swift
文件
2.1 添加 Swift AWS Lambda Runtime 作为依赖项
swift package add-dependency https://github.com/swift-server/swift-aws-lambda-runtime.git --branch main
swift package add-target-dependency AWSLambdaRuntime MyLambda --package swift-aws-lambda-runtime
2.2(可选 - 仅在 macOS 上)在 name
之后添加 platforms
platforms: [.macOS(.v15)],
2.3 您的 Package.swift
文件必须如下所示
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "MyLambda",
platforms: [.macOS(.v15)],
dependencies: [
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main"),
],
targets: [
.executableTarget(
name: "MyLambda",
dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
]
),
]
)
搭建一个最小的 Lambda 函数
runtime 附带一个插件,用于生成简单的 AWS Lambda 函数的代码
swift package lambda-init --allow-writing-to-package-directory
您的 Sources/main.swift
文件必须如下所示。
import AWSLambdaRuntime
// in this example we are receiving and responding with strings
let runtime = LambdaRuntime {
(event: String, context: LambdaContext) in
return String(event.reversed())
}
try await runtime.run()
runtime 附带一个插件,用于在 Amazon Linux 上编译并创建一个 ZIP 存档
swift package archive --allow-network-connections docker
如果没有错误,则 ZIP 存档已准备好部署。ZIP 文件位于 .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/MyLambda.zip
有多种部署到 AWS 的方法(SAM、 Terraform、 AWS Cloud Development Kit (CDK)、 AWS 控制台),这些将在本文档后面介绍。
以下是如何使用 aws
命令行进行部署。
aws lambda create-function \
--function-name MyLambda \
--zip-file fileb://.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/MyLambda.zip \
--runtime provided.al2 \
--handler provided \
--architectures arm64 \
--role arn:aws:iam::<YOUR_ACCOUNT_ID>:role/lambda_basic_execution
只有在 Apple Silicon 机器(Apple M1 或更新版本)上构建二进制文件时,才需要 --architectures
标志。它默认为 x64
。
请务必将 <YOUR_ACCOUNT_ID> 替换为您的实际 AWS 账户 ID(例如:012345678901)。
重要
在创建函数之前,您需要在您的 AWS 账户中拥有一个 lambda_basic_execution
IAM 角色。
您可以通过两种方式创建此角色
Examples/_MyFirstFunction/create_iam_role.sh
中的 create_lambda_execution_role()
函数中运行命令aws lambda invoke \
--function-name MyLambda \
--payload $(echo \"Hello World\" | base64) \
out.txt && cat out.txt && rm out.txt
这应该打印
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
"dlroW olleH"
通常,您的 Lambda 函数将接收一个表示为 JSON 的输入参数,并将使用另一个 JSON 进行响应。当您的 Lambda 函数处理程序接受符合 Decodable
协议并返回符合 Encodable
协议的类型时,Swift AWS Lambda runtime 会自动处理 JSON 对象的编码和解码。
以下是一个最小函数的示例,该函数接受 JSON 对象作为输入,并使用另一个 JSON 对象进行响应。
import AWSLambdaRuntime
// the data structure to represent the input parameter
struct HelloRequest: Decodable {
let name: String
let age: Int
}
// the data structure to represent the output response
struct HelloResponse: Encodable {
let greetings: String
}
// the Lambda runtime
let runtime = LambdaRuntime {
(event: HelloRequest, context: LambdaContext) in
HelloResponse(
greetings: "Hello \(event.name). You look \(event.age > 30 ? "younger" : "older") than your age."
)
}
// start the loop
try await runtime.run()
您可以在 Hello JSON 示例 README 文件中了解如何部署和调用此函数。
您可以配置您的 Lambda 函数以将响应负载流式传输回客户端。响应流式传输可以通过提高首字节时间 (TTFB) 性能来使延迟敏感型应用程序受益。这是因为您可以随着部分响应变得可用而将其发送回客户端。此外,您可以使用响应流式传输来构建返回较大负载的函数。响应流负载的软限制为 20 MB,而缓冲响应的限制为 6 MB。流式传输响应还意味着您的函数不需要将整个响应都放入内存中。对于非常大的响应,这可以减少您需要为函数配置的内存量。
流式响应会产生费用。有关更多信息,请参阅 AWS Lambda 定价。
您可以通过 Lambda 函数 URL、AWS SDK 或使用 Lambda InvokeWithResponseStream API 来流式传输响应。在此示例中,我们创建一个经过身份验证的 Lambda 函数 URL。
以下是一个最小函数的示例,该函数以每秒一个数字的间隔流式传输 10 个数字。
import AWSLambdaRuntime
import NIOCore
struct SendNumbersWithPause: StreamingLambdaHandler {
func handle(
_ event: ByteBuffer,
responseWriter: some LambdaResponseStreamWriter,
context: LambdaContext
) async throws {
for i in 1...10 {
// Send partial data
try await responseWriter.write(ByteBuffer(string: "\(i)\n"))
// Perform some long asynchronous work
try await Task.sleep(for: .milliseconds(1000))
}
// All data has been sent. Close off the response stream.
try await responseWriter.finish()
}
}
let runtime = LambdaRuntime.init(handler: SendNumbersWithPause())
try await runtime.run()
您可以在 流式示例 README 文件中了解如何部署和调用此函数。
大多数 Lambda 函数由源自其他 AWS 服务(如 Amazon SNS
、Amazon SQS
或 AWS APIGateway
)的事件触发。
Swift AWS Lambda Events 包包含一个 AWSLambdaEvents
模块,该模块为大多数常见的 AWS 事件类型提供了实现,进一步简化了 Lambda 函数的编写。
以下是一个示例 Lambda 函数,当 AWS APIGateway 接收到 HTTP 请求时调用该函数。
import AWSLambdaEvents
import AWSLambdaRuntime
let runtime = LambdaRuntime {
(event: APIGatewayV2Request, context: LambdaContext) -> APIGatewayV2Response in
APIGatewayV2Response(statusCode: .ok, body: "Hello World!")
}
try await runtime.run()
您可以在 API Gateway 示例 README 文件中了解如何部署和调用此函数。
目前正在实施对 Swift Service Lifecycle 的支持。您可以关注 #374 以获取更多详细信息和当前状态。欢迎您的贡献。
后台任务允许代码在主响应返回后异步执行,从而实现额外的处理,而不会影响响应延迟。这种方法非常适合诸如日志记录、数据更新或通知等可以延迟的场景。该代码利用 Lambda 的“响应流式传输”功能,该功能有效地平衡了实时用户响应能力与在响应后执行扩展任务的能力。有关 Lambda 后台任务的更多信息,请参阅 这篇 AWS 博客文章。
以下是一个最小函数的示例,该函数在返回响应后但在处理程序返回之前等待 10 秒。
import AWSLambdaRuntime
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif
struct BackgroundProcessingHandler: LambdaWithBackgroundProcessingHandler {
struct Input: Decodable {
let message: String
}
struct Greeting: Encodable {
let echoedMessage: String
}
typealias Event = Input
typealias Output = Greeting
func handle(
_ event: Event,
outputWriter: some LambdaResponseWriter<Output>,
context: LambdaContext
) async throws {
// Return result to the Lambda control plane
context.logger.debug("BackgroundProcessingHandler - message received")
try await outputWriter.write(Greeting(echoedMessage: event.message))
// Perform some background work, e.g:
context.logger.debug("BackgroundProcessingHandler - response sent. Performing background tasks.")
try await Task.sleep(for: .seconds(10))
// Exit the function. All asynchronous work has been executed before exiting the scope of this function.
// Follows structured concurrency principles.
context.logger.debug("BackgroundProcessingHandler - Background tasks completed. Returning")
return
}
}
let adapter = LambdaCodableAdapter(handler: BackgroundProcessingHandler())
let runtime = LambdaRuntime.init(handler: adapter)
try await runtime.run()
您可以在 后台任务示例 README 文件中了解如何部署和调用此函数。
在将代码部署到 AWS Lambda 之前,您可以通过在本地计算机上运行可执行目标来在本地对其进行测试。它在 CLI 上看起来像这样
swift run
当不在 Lambda 执行环境内运行时,它会启动一个本地 HTTP 服务器,监听端口 7000。您可以通过向 http://127.0.0.1:7000/invoke
发送 HTTP POST 请求来调用您的本地 Lambda 函数。
该请求必须包含您的函数预期作为 event
的 JSON 负载。您可以创建一个文本文件,其中包含 AWS 记录的 JSON 负载或从跟踪中捕获的 JSON 负载。在此示例中,我们使用了 文档中的 APIGatewayv2 JSON 负载,并将其保存为 events/create-session.json
文本文件。
然后我们使用 curl 使用测试 JSON 负载调用本地端点。
curl -v --header "Content-Type:\ application/json" --data @events/create-session.json http://127.0.0.1:7000/invoke
* Trying 127.0.0.1:7000...
* Connected to 127.0.0.1 (127.0.0.1) port 7000
> POST /invoke HTTP/1.1
> Host: 127.0.0.1:7000
> User-Agent: curl/8.4.0
> Accept: */*
> Content-Type:\ application/json
> Content-Length: 1160
>
< HTTP/1.1 200 OK
< content-length: 247
<
* Connection #0 to host 127.0.0.1 left intact
{"statusCode":200,"isBase64Encoded":false,"body":"...","headers":{"Access-Control-Allow-Origin":"*","Content-Type":"application\/json; charset=utf-8","Access-Control-Allow-Headers":"*"}}
默认情况下,当使用本地 Lambda 服务器时,它会监听 /invoke
端点。
某些测试工具(例如 AWS Lambda runtime interface emulator)需要不同的端点。在这种情况下,您可以使用 LOCAL_LAMBDA_SERVER_INVOCATION_ENDPOINT
环境变量来强制 runtime 监听不同的端点。
示例
LOCAL_LAMBDA_SERVER_INVOCATION_ENDPOINT=/2015-03-31/functions/function/invocations swift run
文档 中提供了完整的部署指南。
有多种方法可以将您的 Swift 代码部署到 AWS Lambda。第一次,您可能会使用 AWS 控制台创建一个新的 Lambda 函数并将您的代码作为 zip 文件上传。但是,当您迭代您的代码时,您会希望自动化部署过程。
为了充分利用云,我们建议使用基础设施即代码 (IaC) 工具,如 AWS Serverless Application Model (SAM) 或 AWS Cloud Development Kit (CDK)。这些工具允许您将基础设施和部署过程定义为代码,这些代码可以进行版本控制和自动化。
或者,您也可以考虑使用流行的第三方工具,如 Serverless Framework、 Terraform 或 Pulumi 来部署 Lambda 函数并创建和管理 AWS 基础设施。
以下是一个简短的示例,展示了如何使用 SAM 进行部署。
先决条件:安装 SAM CLI
使用 SAM 时,您需要在 YAML 文本文件中描述您的部署。API Gateway 示例目录包含一个名为 template.yaml
的文件,您可以将其用作起点。
要部署您的 Lambda 函数并创建基础设施,请键入以下 sam
命令。
sam deploy \
--resolve-s3 \
--template-file template.yaml \
--stack-name APIGatewayLambda \
--capabilities CAPABILITY_IAM
在部署结束时,脚本会列出 API Gateway 端点。输出类似于以下内容。
-----------------------------------------------------------------------------------------------------------------------------
Outputs
-----------------------------------------------------------------------------------------------------------------------------
Key APIGatewayEndpoint
Description API Gateway endpoint URL"
Value https://a5q74es3k2.execute-api.us-east-1.amazonaws.com
-----------------------------------------------------------------------------------------------------------------------------
有关更多详细信息,请参阅文档 中提供的完整部署指南。
设计文档详细介绍了 swift-aws-lambda-runtime 库的 v2 API 提案,该提案旨在增强使用 Swift 构建 Serverless 函数的开发者体验。
该提案已进行审查,并采纳了社区的反馈。完整的 v2 API 设计文档可在此存储库中找到。
v2 API 优先考虑以下原则
可读性和可维护性:广泛使用 async
/await
提高了代码清晰度并简化了维护。
开发者控制:开发者拥有 main()
函数,并且可以灵活地将依赖项注入到 LambdaRuntime
中。这使您可以使用 Swift Service Lifecycle 有效地管理服务生命周期以实现结构化并发。
简化的 Codable 支持:LambdaCodableAdapter
结构消除了在编码和解码事件和响应时对冗长样板代码的需求。
v2 API 引入了两项新功能
响应流式传输:此功能非常适合处理需要增量发送的大型响应。
后台工作:计划在向 AWS Lambda 控制平面返回响应后运行的任务。
这些新功能在使用 swift-aws-lambda-runtime 库在 Swift 中构建 Serverless 函数时提供了更大的灵活性和控制力。