定义采用 Go 的 Lambda 函数处理程序 - AWS Lambda

定义采用 Go 的 Lambda 函数处理程序

Lambda 函数处理程序是函数代码中处理事件的方法。当调用函数时,Lambda 运行处理程序方法。您的函数会一直运行,直到处理程序返回响应、退出或超时。

Go 处理程序基础知识

Go 中编写的 Lambda 函数被编写为 Go 可执行文件。在 Lambda 函数代码中,需要包含 github.com/aws/aws-lambda-go/lambda 程序包,该程序包将实现适用于 Go 的 Lambda 编程模型。此外,您需要实现处理程序函数代码和 main() 函数。

例 Go Lambda 函数
package main import ( "context" "fmt" "github.com/aws/aws-lambda-go/lambda" ) type MyEvent struct { Name string `json:"name"` } func HandleRequest(ctx context.Context, event *MyEvent) (*string, error) { if event == nil { return nil, fmt.Errorf("received nil event") } message := fmt.Sprintf("Hello %s!", event.Name) return &message, nil } func main() { lambda.Start(HandleRequest) }

下面是此函数的输入示例:

{ "name": "Jane" }

请注意以下几点:

  • package main:在 Go 中,包含 func main() 的程序包必须始终名为 main

  • import:您可以使用它来包含 Lambda 函数所需的库。在此实例中,它包括:

  • func HandleRequest(ctx context.Context, event *MyEvent) (*string, error):这是您的 Lambda 处理程序签名。它是 Lambda 函数的入口点,包含调用函数时执行的逻辑。此外,包含的参数表示以下含义:

    • ctx context.Context:为您的 Lambda 函数调用提供运行时信息。ctx 是您声明的变量,用于利用通过 使用 Lambda 上下文对象检索 Go 函数信息 提供的信息。

    • event *MyEvent:这是一个指向 MyEvent 的名为 event 的参数. 它表示 Lambda 函数的输入。

    • *string, error:处理程序返回两个值。第一个是指向包含 Lambda 函数结果的字符串的指针。第二个是错误类型,如果没有错误,则为 nil;如果出错,则包含标准错误信息。

    • return &message, nil:返回两个值。第一个是指向字符串消息的指针,它是使用输入事件中的 Name 字段构造的问候语。第二个值 nil 表示该函数没有遇到任何错误。

  • func main():这是运行您的 Lambda 函数代码的入口点。该项为必填项。

    通过在 func main(){} 代码括号之间添加 lambda.Start(HandleRequest),可以执行您的 Lambda 函数。按照 Go 语言标准,开括号即 { 必须直接置于 main 函数签名末尾。

命名

provided.al2 与 provided.al2023 运行时系统

对于使用 .zip 部署包中的 provided.al2provided.al2023 运行时系统的 Go 函数,包含函数代码的可执行文件必须命名为 bootstrap。如果使用 .zip 文件部署函数,bootstrap 文件必须位于 .zip 文件的根目录中。对于使用容器映像中的 provided.al2provided.al2023 运行时系统的 Go 函数,可执行文件可以使用任何名称。

处理程序可以使用任何名称。要在代码中引用处理程序值,可以使用 _HANDLER 环境变量。

go1.x 运行时系统

对于使用 go1.x 运行时系统的 Go 函数,可执行文件和处理程序可以共享任何名称。例如,如果您将处理程序的值设置为 Handler,Lambda 将在 Handler 可执行文件中调用该 main() 函数。

要在 Lambda 控制台中更改函数处理程序名称,请在 Runtime settings(运行时设置)窗格中,选择 Edit(编辑)。

使用结构化类型的 Lambda 函数处理程序

在上述示例中,输入类型是简单的字符串。但是,您也可以将结构化事件传递到您的函数处理程序:

package main import ( "fmt" "github.com/aws/aws-lambda-go/lambda" ) type MyEvent struct { Name string `json:"What is your name?"` Age int `json:"How old are you?"` } type MyResponse struct { Message string `json:"Answer"` } func HandleLambdaEvent(event *MyEvent) (*MyResponse, error) { if event == nil { return nil, fmt.Errorf("received nil event") } return &MyResponse{Message: fmt.Sprintf("%s is %d years old!", event.Name, event.Age)}, nil } func main() { lambda.Start(HandleLambdaEvent) }

下面是此函数的输入示例:

{ "What is your name?": "Jim", "How old are you?": 33 }

响应类似于以下示例:

{ "Answer": "Jim is 33 years old!" }

若要导出,事件结构中的字段名称必须大写。有关来自AWS事件源的处理事件的更多信息,请参阅 aws-lambda-go/events

有效处理程序签名

在 Go 中构建 Lambda 函数处理程序时,您有多个选项,但您必须遵守以下规则:

  • 处理程序必须为函数。

  • 处理程序可能需要 0 到 2 个参数。如果有两个参数,则第一个参数必须实现 context.Context

  • 处理程序可能返回 0 到 2 个参数。如果有一个返回值,则它必须实现 error。如果有两个返回值,则第二个值必须实现 error

下面列出了有效的处理程序签名。TInTOut 表示类型与 encoding/json 标准库兼容。有关更多信息,请参阅 func Unmarshal,以了解如何反序列化这些类型。

  • func ()
  • func () error
  • func (TIn) error
  • func () (TOut, error)
  • func (context.Context) error
  • func (context.Context, TIn) error
  • func (context.Context) (TOut, error)
  • func (context.Context, TIn) (TOut, error)

使用全局状态

您可以声明并修改独立于 Lambda 函数的处理程序代码的全局变量。此外,您的处理程序可能声明一个 init 函数,该函数在加载您的处理程序时执行。这在 AWS Lambda 中行为方式相同,正如在标准 Go 程序中一样。您的 Lambda 函数的单个实例绝不会同时处理多个事件。

例 具有全局变量的 Go 函数
注意

此代码使用 AWS SDK for Go V2。有关更多信息,请参阅 AWS SDK for Go V2 入门

package main import ( "context" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" "log" ) var invokeCount int var myObjects []types.Object func init() { // Load the SDK configuration cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalf("Unable to load SDK config: %v", err) } // Initialize an S3 client svc := s3.NewFromConfig(cfg) // Define the bucket name as a variable so we can take its address bucketName := "amzn-s3-demo-bucket" input := &s3.ListObjectsV2Input{ Bucket: &bucketName, } // List objects in the bucket result, err := svc.ListObjectsV2(context.TODO(), input) if err != nil { log.Fatalf("Failed to list objects: %v", err) } myObjects = result.Contents } func LambdaHandler(ctx context.Context) (int, error) { invokeCount++ for i, obj := range myObjects { log.Printf("object[%d] size: %d key: %s", i, obj.Size, *obj.Key) } return invokeCount, nil } func main() { lambda.Start(LambdaHandler) }

Go Lambda 函数的代码最佳实践

在构建 Lambda 函数时,请遵循以下列表中的指南,采用最佳编码实践:

  • 从核心逻辑中分离 Lambda 处理程序。这样您可以创建更容易进行单元测试的函数。

  • 将依赖关系的复杂性降至最低。首选在执行环境启动时可以快速加载的更简单的框架。

  • 将部署程序包大小精简为只包含运行时必要的部分。这样会减少调用前下载和解压缩部署程序包所需的时间。

  • 利用执行环境重用来提高函数性能。连接软件开发工具包 (SDK) 客户端和函数处理程序之外的数据库,并在 /tmp 目录中本地缓存静态资产。由函数的同一实例处理的后续调用可重用这些资源。这样就可以通过缩短函数运行时间来节省成本。

    为了避免调用之间潜在的数据泄露,请不要使用执行环境来存储用户数据、事件或其他具有安全影响的信息。如果您的函数依赖于无法存储在处理程序的内存中的可变状态,请考虑为每个用户创建单独的函数或单独的函数版本。

  • 使用 keep-alive 指令来维护持久连接。Lambda 会随着时间的推移清除空闲连接。在调用函数时尝试重用空闲连接会导致连接错误。要维护您的持久连接,请使用与运行时关联的 keep-alive 指令。有关示例,请参阅在 Node.js 中通过 Keep-Alive 重用连接

  • 使用环境变量将操作参数传递给函数。例如,您在写入 Amazon S3 存储桶时,不应对要写入的存储桶名称进行硬编码,而应将存储桶名称配置为环境变量。

  • 避免在 Lambda 函数中使用递归代码,因为如果使用递归代码,函数便会自动调用自身,直到满足某些任意条件为止。这可能会导致意想不到的函数调用量和升级成本。如果您不慎执行此操作,请立即将函数保留并发设置为 0 来限制对函数的所有调用,同时更新代码。

  • Lambda 函数代码中不要使用非正式的非公有 API。对于 AWS Lambda 托管式运行时,Lambda 会定期为 Lambda 的内部 API 应用安全性和功能更新。这些内部 API 更新可能不能向后兼容,会导致意外后果,例如,假设您的函数依赖于这些非公有 API,则调用会失败。请参阅 API 参考以查看公开发布的 API 列表。

  • 编写幂等代码。为您的函数编写幂等代码可确保以相同的方式处理重复事件。您的代码应该正确验证事件并优雅地处理重复事件。有关更多信息,请参阅如何使我的 Lambda 函数具有幂等性?