重要

此处包含的文档指的是 Swift AWS Lambda Runtime v2(来自 main 分支的代码)。如果您正在为 runtime v1.x 开发,请查看此 readme

本指南包含以下章节

Swift AWS Lambda Runtime

许多现代系统都有客户端组件(如 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 AWS Lambda runtime 教程。它为开发人员提供了详细的分步说明,以开发、构建和部署 Lambda 函数。

我们还编写了一份全面的部署指南

或者,如果您迫不及待地想开始使用 runtime v2,请尝试以下六个步骤

Examples/_MyFirstFunction 包含一个脚本,该脚本将完成本节中描述的步骤。

如果您真的迫不及待,只需输入

cd Examples/_MyFirstFunction
./create_and_deploy_function.sh

否则,请继续阅读。

  1. 创建一个新的 Swift 可执行项目
mkdir MyLambda && cd MyLambda
swift package init --type executable
  1. 准备您的 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"),
                ]
                ),
        ]
    )
  2. 搭建一个最小的 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()
  1. 构建并归档包

runtime 附带一个插件,用于在 Amazon Linux 上编译并创建一个 ZIP 存档

swift package archive --allow-network-connections docker

如果没有错误,则 ZIP 存档已准备好部署。ZIP 文件位于 .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/MyLambda.zip

  1. 部署到 AWS

有多种部署到 AWS 的方法(SAMTerraformAWS 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 角色。

您可以通过两种方式创建此角色

  1. 使用 AWS 控制台
  2. Examples/_MyFirstFunction/create_iam_role.sh 中的 create_lambda_execution_role() 函数中运行命令
  1. 调用您的 Lambda 函数
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"

开发您的 Swift Lambda 函数

接收 JSON 对象并使用 JSON 对象响应

通常,您的 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 流式响应

您可以配置您的 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 文件中了解如何部署和调用此函数。

与 AWS 服务集成

大多数 Lambda 函数由源自其他 AWS 服务(如 Amazon SNSAmazon SQSAWS 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 集成

目前正在实施对 Swift Service Lifecycle 的支持。您可以关注 #374 以获取更多详细信息和当前状态。欢迎您的贡献。

使用 Lambda 后台任务

后台任务允许代码在主响应返回后异步执行,从而实现额外的处理,而不会影响响应延迟。这种方法非常适合诸如日志记录、数据更新或通知等可以延迟的场景。该代码利用 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 Lambda 函数

文档 中提供了完整的部署指南

有多种方法可以将您的 Swift 代码部署到 AWS Lambda。第一次,您可能会使用 AWS 控制台创建一个新的 Lambda 函数并将您的代码作为 zip 文件上传。但是,当您迭代您的代码时,您会希望自动化部署过程。

为了充分利用云,我们建议使用基础设施即代码 (IaC) 工具,如 AWS Serverless Application Model (SAM)AWS Cloud Development Kit (CDK)。这些工具允许您将基础设施和部署过程定义为代码,这些代码可以进行版本控制和自动化。

或者,您也可以考虑使用流行的第三方工具,如 Serverless FrameworkTerraformPulumi 来部署 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 - 设计原则

设计文档详细介绍了 swift-aws-lambda-runtime 库的 v2 API 提案,该提案旨在增强使用 Swift 构建 Serverless 函数的开发者体验。

该提案已进行审查,并采纳了社区的反馈。完整的 v2 API 设计文档可在此存储库中找到。

关键设计原则

v2 API 优先考虑以下原则

新功能

v2 API 引入了两项新功能

响应流式传输:此功能非常适合处理需要增量发送的大型响应。  

后台工作:计划在向 AWS Lambda 控制平面返回响应后运行的任务。

这些新功能在使用 swift-aws-lambda-runtime 库在 Swift 中构建 Serverless 函数时提供了更大的灵活性和控制力。