在Go语言的AWS Lambda中,你的设计模式是合理的,体现了依赖注入和结构化的思想。以下是对你当前模式的评估和示例代码:
当前模式分析
你的Application结构体封装了Lambda函数所需的共享依赖,这是Go中常见的做法。将Lambda客户端、ARN映射、环境变量和日志记录器集中管理,有利于代码组织和测试。
改进建议和示例
1. 日志记录器处理
你可以在初始化时创建基础日志记录器,然后在每个请求中创建带有请求上下文的子记录器:
type LambdaApp struct {
*lu.Application
baseLog zerolog.Logger
}
func (app *LambdaApp) handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// 为每个请求创建带上下文的日志记录器
requestLog := app.baseLog.With().
Str("requestId", request.RequestContext.RequestID).
Str("path", request.Path).
Str("method", request.HTTPMethod).
Logger()
// 将请求特定的日志记录器传递给业务逻辑
return app.handleBusinessLogic(ctx, request, requestLog)
}
func (app *LambdaApp) handleBusinessLogic(ctx context.Context, request events.APIGatewayProxyRequest, log zerolog.Logger) (events.APIGatewayProxyResponse, error) {
log.Info().Msg("处理请求")
// 业务逻辑代码
return events.APIGatewayProxyResponse{
StatusCode: 200,
Body: "成功",
}, nil
}
2. 上下文传递模式
避免在结构体中存储上下文,而是作为参数传递:
func (app *LambdaApp) validateRequest(ctx context.Context, log zerolog.Logger, request events.APIGatewayProxyRequest) error {
log.Info().Msg("验证请求")
// 使用ctx进行超时控制或取消
select {
case <-ctx.Done():
return ctx.Err()
default:
// 正常处理
}
// 验证逻辑
if request.Body == "" {
return errors.New("请求体为空")
}
return nil
}
3. 完整的初始化示例
type LambdaApp struct {
LambdaClient awsutils.LambdaClientInterface
Arn map[lu.ServiceArn]string
Config map[string]string
BaseLogger zerolog.Logger
}
func NewLambdaApp(lambdaClient awsutils.LambdaClientInterface, serviceArns []lu.ServiceArn, envs []string) *LambdaApp {
app := &LambdaApp{
LambdaClient: lambdaClient,
BaseLogger: zerolog.New(os.Stdout).With().Timestamp().Logger(),
}
// 初始化ARN映射
app.Arn = make(map[lu.ServiceArn]string)
for _, arn := range serviceArns {
app.Arn[arn] = os.Getenv(string(arn))
}
// 初始化环境变量
app.Config = make(map[string]string)
for _, env := range envs {
app.Config[env] = os.Getenv(env)
}
return app
}
func (app *LambdaApp) Start() {
lambda.Start(app.handler)
}
func (app *LambdaApp) handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
// 创建请求特定的日志记录器
requestLogger := app.BaseLogger.With().
Str("requestId", request.RequestContext.RequestID).
Str("path", request.Path).
Logger()
// 调用其他Lambda的示例
if app.LambdaClient != nil {
payload := []byte(`{"action": "process"}`)
_, err := app.LambdaClient.Invoke(ctx, &lambda.InvokeInput{
FunctionName: aws.String(app.Arn[lu.XYZ]),
Payload: payload,
InvocationType: aws.String("RequestResponse"),
})
if err != nil {
requestLogger.Error().Err(err).Msg("调用Lambda失败")
return events.APIGatewayProxyResponse{
StatusCode: 500,
Body: "内部错误",
}, nil
}
}
// 执行业务逻辑
result, err := app.processRequest(ctx, requestLogger, request)
if err != nil {
return events.APIGatewayProxyResponse{
StatusCode: 400,
Body: err.Error(),
}, nil
}
return events.APIGatewayProxyResponse{
StatusCode: 200,
Body: result,
}, nil
}
func (app *LambdaApp) processRequest(ctx context.Context, log zerolog.Logger, request events.APIGatewayProxyRequest) (string, error) {
log.Info().Msg("处理请求开始")
// 使用配置
whatever := app.Config["WHATEVER"]
log.Debug().Str("whatever", whatever).Msg("配置值")
// 业务逻辑
return "处理完成", nil
}
func main() {
lambdaClient := awsutils.NewLambdaClient()
serviceArns := []lu.ServiceArn{lu.XYZ, lu.ABC}
otherEnvs := []string{"WHATEVER", "ANOTHER_ENV"}
app := NewLambdaApp(lambdaClient, serviceArns, otherEnvs)
app.Start()
}
4. 中间件模式
考虑使用中间件来处理横切关注点:
type HandlerFunc func(context.Context, events.APIGatewayProxyRequest, zerolog.Logger) (events.APIGatewayProxyResponse, error)
func (app *LambdaApp) withLogging(next HandlerFunc) HandlerFunc {
return func(ctx context.Context, request events.APIGatewayProxyRequest, log zerolog.Logger) (events.APIGatewayProxyResponse, error) {
start := time.Now()
log.Info().Msg("请求开始")
response, err := next(ctx, request, log)
duration := time.Since(start)
log.Info().
Dur("duration", duration).
Int("status", response.StatusCode).
Msg("请求完成")
return response, err
}
}
func (app *LambdaApp) handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
log := app.BaseLogger.With().
Str("requestId", request.RequestContext.RequestID).
Logger()
handler := app.withLogging(app.handleBusinessLogic)
return handler(ctx, request, log)
}
你的当前设计已经走在正确的道路上,主要改进点在于避免在结构体中存储请求特定的数据(如上下文),而是通过参数传递。这样可以避免并发问题,并使代码更清晰。