func init() vs func main() 用于初始化 AWS Lambda 处理程序中的全局状态

func init() vs func main() for initalizing global state in AWS Lambda handlers

查看官方 Go 中的 AWS Lambda 函数处理程序 文档中的 使用全局状态 部分 https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html

建议初始化 func init() 中的所有全局状态 即,我们希望在多个 lambda 调用中共享的任何包级变量都放在此处。
我的理解是,每次启动 lambda 容器(即冷启动)时都会进行一次初始化。

我的问题是,是否可以使用 func main() 而不是 func init() 来做同样的事情。
由于 func init() 运行.
的副作用,使用 func init() 基本上使我的处理函数 (func LambdaHandler) 不可单元测试 将 func init() 代码移动到 func main() 似乎很容易解决这个问题。
使用 func main()func init()

有任何副作用吗

代码示例

使用 func init()

package main
 
import (
        "log"
        "github.com/aws/aws-lambda-go/lambda"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3"
        "github.com/aws/aws-sdk-go/aws"
)
 
var invokeCount = 0
var myObjects []*s3.Object
func init() {
        svc := s3.New(session.New())
        input := &s3.ListObjectsV2Input{
                Bucket: aws.String("examplebucket"),
        }
        result, _ := svc.ListObjectsV2(input)
        myObjects = result.Contents
}
 
func LambdaHandler() (int, error) {
        invokeCount = invokeCount + 1
        log.Print(myObjects)
        return invokeCount, nil
}
 
func main() {
        lambda.Start(LambdaHandler)
}

vs

使用 func main()

package main
 
import (
        "log"
        "github.com/aws/aws-lambda-go/lambda"
        "github.com/aws/aws-sdk-go/aws/session"
        "github.com/aws/aws-sdk-go/service/s3"
        "github.com/aws/aws-sdk-go/aws"
)
 
var invokeCount = 0
var myObjects []*s3.Object
 
func LambdaHandler() (int, error) {
        invokeCount = invokeCount + 1
        log.Print(myObjects)
        return invokeCount, nil
}
 
func main() {
        svc := s3.New(session.New())
        input := &s3.ListObjectsV2Input{
                Bucket: aws.String("examplebucket"),
        }
        result, _ := svc.ListObjectsV2(input)
        myObjects = result.Contents

        lambda.Start(LambdaHandler)
}

我会提出以下建议(我们在很多 Go Lambda 中成功使用了它)。

main.go

[...]

func (h *handler) handleRequest(ctx context.Context) error {
    input := h.s3Client.ListObjectsV2Input{
        Bucket: aws.String("examplebucket"),
    }

    [...]
}

type handler struct {
    s3Client s3iface.S3API
}

// main is called only once, when the Lambda is initialised (started for the first time). Code in this function should
// primarily be used to create service clients, read environments variables, read configuration from disk etc.
func main() {
    h := handler{
        s3client: s3.New(session.New()),
    }

    lambda.Start(h.handleRequest)
}

main_test.go

type ListObjectsV2Mock struct {
    s3iface.S3API

    output *s3.ListObjectsV2Output
}

func TestHandleRequest(t *testing.T) {
    h := handler{
        s3Client: &ListObjectsV2Mock{
            output: &s3.ListObjectsV2Output{...},
        },
    }

    err := h.HandleRequest(context.TODO())
    if err != nil {
        t.Fatalf("unexpected error: %v", err)
    }
}

显然,缺少很多代码(导入、错误处理等),但这就是它的要点。