DevToolBox免费
博客

AWS Lambda 指南:无服务器函数、API Gateway、DynamoDB、Step Functions 与性能调优

20 分钟阅读作者 DevToolBox Team

AWS Lambda 让你无需配置或管理服务器即可运行代码。你只需为消耗的计算时间付费,Lambda 会自动扩展。本指南涵盖 Lambda 基础、Node.js 和 Python 处理程序模式、API Gateway 集成、事件源、层、DynamoDB 操作、Step Functions、性能调优、测试和监控。

TL;DR: AWS Lambda 是一种无服务器计算服务,响应事件运行代码。支持 Node.js、Python、Java、Go、.NET 和自定义运行时。关键概念:处理函数处理事件,冷启动发生在新容器上,API Gateway 提供 HTTP 端点,S3、DynamoDB Streams 和 SQS 等事件源自动触发函数。

关键要点

  • Lambda 按请求和每 GB 秒计算收费。免费层每月包含 100 万请求和 400,000 GB 秒。
  • 冷启动范围从 100ms(Node.js/Python)到 1-2s(Java/.NET)。使用预置并发消除关键路径的冷启动。
  • 在处理程序外初始化 SDK 客户端和数据库连接,以在热调用间重用。
  • API Gateway + Lambda 是构建无服务器 REST API 最常见的模式。
  • Lambda 层让你在多个函数间共享代码和依赖,无需在每次部署中捆绑。
  • 使用 AWS SAM 或 CDK 进行基础设施即代码。SAM 用无服务器特定快捷方式扩展 CloudFormation。

Lambda 基础

Lambda 在 AWS 管理的短生命周期容器中运行代码。请求到达时,Lambda 重用温暖容器或创建新容器(冷启动)。

执行模型

每个 Lambda 函数有初始化阶段(冷启动)和调用阶段。冷启动增加 100ms 到 2s 的延迟。

// Lambda execution lifecycle
// INIT PHASE (cold start only)
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const client = new DynamoDBClient({}); // reused across invocations

// INVOKE PHASE (every request)
exports.handler = async (event, context) => {
  console.log("Request ID:", context.awsRequestId);
  console.log("Time left:", context.getRemainingTimeInMillis(), "ms");
  return { statusCode: 200, body: "OK" };
};

冷启动因素

因素影响缓解
运行时Node.js/Python: ~100-200ms, Java: ~1-2s选择轻量级运行时
包大小更大的包增加下载时间摇树优化,使用层
VPCVPC 附加增加 1-5s除非需要否则避免 VPC
内存更多内存 = 更多 CPU = 更快初始化分配 512MB+ 以加快启动

Lambda 与 Node.js

Node.js 是最流行的 Lambda 运行时。处理程序接收事件对象和上下文对象。

// index.mjs (ES module handler)
export const handler = async (event, context) => {
  const { httpMethod, path, body, queryStringParameters } = event;

  try {
    const data = JSON.parse(body || "{}");
    return {
      statusCode: 200,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: "Success", input: data }),
    };
  } catch (err) {
    return { statusCode: 500, body: JSON.stringify({ error: err.message }) };
  }
};

Lambda 与 Python

Python 是第二流行的 Lambda 运行时。处理程序模式类似于 Node.js。

# lambda_function.py
import json
import boto3

# Init outside handler (reused on warm starts)
s3 = boto3.client("s3")

def handler(event, context):
    body = json.loads(event.get("body", "{}"))
    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"message": "Hello", "input": body})
    }

API Gateway 集成

API Gateway 提供触发 Lambda 函数的 HTTP 端点,处理路由、请求验证、授权、限流和 CORS。

REST API 配置

使用 Lambda 代理集成时,API Gateway 将整个 HTTP 请求传递给 Lambda。

// API Gateway Lambda Proxy - event structure
// event.httpMethod    -> "GET", "POST", etc.
// event.path          -> "/users/123"
// event.headers       -> { "Content-Type": "..." }
// event.queryStringParameters -> { "page": "1" }
// event.body          -> "{...}" (string)

exports.handler = async (event) => {
  if (event.httpMethod === "GET" && event.path === "/users") {
    return {
      statusCode: 200,
      headers: {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*"
      },
      body: JSON.stringify([{ id: 1, name: "Alice" }])
    };
  }
  return { statusCode: 404, body: "Not Found" };
};

事件源

Lambda 可由多种 AWS 服务触发,每个事件源提供不同的事件结构。

S3 事件

当 S3 存储桶中的对象被创建、修改或删除时触发 Lambda。

// S3 event handler
exports.handler = async (event) => {
  for (const record of event.Records) {
    const bucket = record.s3.bucket.name;
    const key = decodeURIComponent(record.s3.object.key);
    console.log("New object:", bucket, key);
    // Process the uploaded file
  }
};

DynamoDB Streams

实时处理 DynamoDB 表的更改。每个流记录包含旧的和新的项目映像。

// DynamoDB Streams handler
exports.handler = async (event) => {
  for (const record of event.Records) {
    const { eventName, dynamodb } = record;
    if (eventName === "INSERT") {
      console.log("New item:", JSON.stringify(dynamodb.NewImage));
    } else if (eventName === "MODIFY") {
      console.log("Updated:", JSON.stringify(dynamodb.NewImage));
    }
  }
};

SQS

处理 SQS 队列中的消息。Lambda 轮询队列并批量调用函数。

// SQS handler with batch processing
exports.handler = async (event) => {
  const failures = [];
  for (const record of event.Records) {
    try {
      const body = JSON.parse(record.body);
      await processMessage(body);
    } catch (err) {
      failures.push({ itemIdentifier: record.messageId });
    }
  }
  return { batchItemFailures: failures };
};

Lambda 层和依赖

层让你将库、自定义运行时和配置文件与函数代码分开打包。函数最多可使用 5 个层。

# Create a Lambda Layer
mkdir -p layer/nodejs && cd layer/nodejs
npm init -y
npm install sharp axios
cd ..
zip -r my-layer.zip nodejs/

# Publish the layer
aws lambda publish-layer-version \
  --layer-name my-deps \
  --zip-file fileb://my-layer.zip \
  --compatible-runtimes nodejs20.x

# Attach layer to function
aws lambda update-function-configuration \
  --function-name my-func \
  --layers arn:aws:lambda:us-east-1:123:layer:my-deps:1

环境变量和密钥

使用环境变量进行配置,使用 Parameter Store 或 Secrets Manager 存储敏感值。

// Access env vars and secrets
const { SSMClient, GetParameterCommand } = require("@aws-sdk/client-ssm");
const ssm = new SSMClient({});

// Simple env var (set in Lambda config)
const TABLE = process.env.TABLE_NAME;

// Cached secret from Parameter Store
let dbPassword;
async function getSecret() {
  if (!dbPassword) {
    const resp = await ssm.send(new GetParameterCommand({
      Name: "/myapp/db-password",
      WithDecryption: true
    }));
    dbPassword = resp.Parameter.Value;
  }
  return dbPassword;
}

Lambda 与 DynamoDB

DynamoDB 是 Lambda 最常用的数据库,因为它无缝扩展且具有个位数毫秒延迟。

const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand, GetCommand,
  QueryCommand, DeleteCommand } = require("@aws-sdk/lib-dynamodb");

const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const TABLE = process.env.TABLE_NAME;

exports.handler = async (event) => {
  // CREATE
  await ddb.send(new PutCommand({
    TableName: TABLE,
    Item: { pk: "user#1", sk: "profile", name: "Alice" }
  }));
  // READ
  const { Item } = await ddb.send(new GetCommand({
    TableName: TABLE, Key: { pk: "user#1", sk: "profile" }
  }));
  // QUERY
  const { Items } = await ddb.send(new QueryCommand({
    TableName: TABLE,
    KeyConditionExpression: "pk = :pk",
    ExpressionAttributeValues: { ":pk": "user#1" }
  }));
  return { statusCode: 200, body: JSON.stringify(Items) };
};

Step Functions

Step Functions 将多个 Lambda 函数编排成工作流,处理重试、错误处理和并行执行。

{
  "Comment": "Order processing workflow",
  "StartAt": "ValidateOrder",
  "States": {
    "ValidateOrder": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123:function:validate",
      "Next": "ProcessPayment",
      "Catch": [{
        "ErrorEquals": ["ValidationError"],
        "Next": "OrderFailed"
      }]
    },
    "ProcessPayment": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123:function:payment",
      "Next": "SendConfirmation"
    },
    "SendConfirmation": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123:function:notify",
      "End": true
    },
    "OrderFailed": { "Type": "Fail", "Error": "OrderFailed" }
  }
}

性能优化

Lambda 性能取决于内存分配、包大小和初始化代码。内存与 CPU 能力线性扩展。

预置并发

预热执行环境以消除冷启动。用于延迟敏感的 API。

# Enable provisioned concurrency
aws lambda put-provisioned-concurrency-config \
  --function-name my-api \
  --qualifier prod \
  --provisioned-concurrent-executions 10

# Auto-scale provisioned concurrency
# Use Application Auto Scaling to adjust
# based on utilization (target: 70%)

内存调优

Lambda 按内存比例分配 CPU。1769 MB 获得 1 个完整 vCPU。

内存vCPU适用场景
128 MB0.07简单路由/代理
512 MB0.29API + 数据库查询
1769 MB1.0数据处理/转换
3008 MB1.7图片/视频处理
10240 MB6.0ML 推理

测试 Lambda 函数

使用 SAM CLI 进行本地测试,使用 AWS SDK 进行集成测试。

# Install SAM CLI
brew install aws-sam-cli

# Invoke locally with test event
sam local invoke MyFunction -e events/api.json

# Start local API Gateway
sam local start-api
# => http://127.0.0.1:3000/users

# Generate sample events
sam local generate-event s3 put \
  --bucket my-bucket --key test.txt > events/s3.json

# Run unit tests (Jest example)
# test/handler.test.js
# const { handler } = require("../index");
# test("returns 200", async () => {
#   const event = { httpMethod: "GET", path: "/users" };
#   const result = await handler(event);
#   expect(result.statusCode).toBe(200);
# });

使用 SAM 的基础设施即代码

AWS SAM 用无服务器特定资源类型扩展 CloudFormation。

# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 30
    Runtime: nodejs20.x
    MemorySize: 512

Resources:
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/index.handler
      Events:
        GetUsers:
          Type: Api
          Properties:
            Path: /users
            Method: get
      Environment:
        Variables:
          TABLE_NAME: !Ref UsersTable
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable

  UsersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: users
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: pk
          AttributeType: S
      KeySchema:
        - AttributeName: pk
          KeyType: HASH

监控和调试

Lambda 与 CloudWatch 集成用于日志和指标,与 X-Ray 集成用于分布式追踪。

CloudWatch 指标

Lambda 发出调用次数、持续时间、错误、限流和并发执行的指标。

// Structured logging for CloudWatch
exports.handler = async (event, context) => {
  console.log(JSON.stringify({
    level: "INFO",
    requestId: context.awsRequestId,
    message: "Processing request",
    path: event.path,
    method: event.httpMethod
  }));

  // CloudWatch Embedded Metric Format
  console.log(JSON.stringify({
    _aws: { Timestamp: Date.now(),
      CloudWatchMetrics: [{
        Namespace: "MyApp",
        Dimensions: [["Function"]],
        Metrics: [{ Name: "ProcessingTime", Unit: "Milliseconds" }]
      }]
    },
    Function: context.functionName,
    ProcessingTime: 42
  }));
};

X-Ray 追踪

X-Ray 跨 Lambda、API Gateway、DynamoDB 追踪请求。在函数配置中启用主动追踪。

# Enable X-Ray tracing via CLI
aws lambda update-function-configuration \
  --function-name my-func \
  --tracing-config Mode=Active

# SAM template
# Properties:
#   Tracing: Active

# In code: wrap AWS SDK calls for tracing
# const AWSXRay = require("aws-xray-sdk-core");
# const AWS = AWSXRay.captureAWS(require("aws-sdk"));

Lambda 限制和配额

了解 Lambda 限制有助于设计在服务边界内的函数。以下是最常遇到的限制。

资源默认限制可调整
并发执行每区域 1,000是(最高数万)
超时15 分钟(900 秒)否(硬限制)
内存128 MB 到 10,240 MB
部署包(zip)直接上传 50 MB,解压 250 MB否(使用容器镜像)
临时存储(/tmp)512 MB 到 10,240 MB可配置
环境变量大小总共 4 KB
每函数层数5

错误处理模式

正确的错误处理对 Lambda 可靠性至关重要。同步调用(API Gateway)和异步调用(S3、SNS)的处理方式不同。同步返回结构化错误响应,异步配置死信队列和 Lambda 目标。

// Robust error handling pattern
class AppError extends Error {
  constructor(message, statusCode, errorCode) {
    super(message);
    this.statusCode = statusCode;
    this.errorCode = errorCode;
  }
}

exports.handler = async (event, context) => {
  try {
    const result = await processRequest(event);
    return {
      statusCode: 200,
      body: JSON.stringify(result)
    };
  } catch (err) {
    console.error(JSON.stringify({
      level: "ERROR",
      requestId: context.awsRequestId,
      error: err.message,
      code: err.errorCode || "INTERNAL_ERROR"
    }));
    const statusCode = err.statusCode || 500;
    return {
      statusCode,
      body: JSON.stringify({
        error: err.errorCode || "INTERNAL_ERROR",
        message: statusCode < 500 ? err.message : "Internal error"
      })
    };
  }
};

部署最佳实践

使用别名和版本进行安全部署。Lambda 版本是函数代码和配置的不可变快照。别名是指向版本的指针,支持流量转移和回滚。

  • 开发使用 $LATEST,暂存/生产使用编号版本
  • 创建别名(dev、staging、prod)指向特定版本
  • 使用加权别名进行金丝雀部署(90% v1, 10% v2)
  • 启用 CodeDeploy 钩子进行流量转移前后验证
  • 将 API Gateway 阶段绑定到 Lambda 别名,而非 $LATEST
  • 使用 SAM 管道或 GitHub Actions 自动化部署
# Publish a new version
aws lambda publish-version --function-name my-func

# Create/update alias pointing to version 5
aws lambda create-alias \
  --function-name my-func \
  --name prod --function-version 5

# Canary deployment: shift 10% traffic to v6
aws lambda update-alias \
  --function-name my-func \
  --name prod --function-version 6 \
  --routing-config AdditionalVersionWeights={"5"=0.9}

常见问题

Lambda 的最大执行时间是多少?

Lambda 函数最多可运行 15 分钟。对于更长的工作负载,使用 Step Functions 或 ECS/Fargate。

AWS Lambda 费用多少?

按请求(每百万 $0.20)和每 GB 秒收费。免费层每月包含 100 万请求。

如何减少冷启动时间?

使用轻量级运行时,最小化包大小,避免不必要的 VPC,分配更多内存,使用预置并发。

Lambda 能连接关系数据库吗?

可以,但使用 RDS Proxy 管理连接池。否则 Lambda 可能耗尽数据库连接。

最大部署包大小是多少?

直接上传 50 MB(压缩),含层 250 MB(解压)。容器镜像最大 10 GB。

Lambda 层如何工作?

层是包含库的 ZIP 归档,提取到 /opt。函数最多引用 5 个层。

同步和异步调用有什么区别?

同步调用等待完成并返回结果。异步调用将事件入队并立即返回。

如何处理 Lambda 中的错误?

同步调用返回 HTTP 状态码。异步调用配置死信队列或 Lambda 目标。使用结构化日志调试。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON FormatterY→YAML to JSON ConverterJWTJWT Decoder

相关文章

DevOps 流水线指南:CI/CD、GitHub Actions、Docker、基础设施即代码与部署策略

完整的 DevOps 流水线指南,涵盖 CI/CD 基础、GitHub Actions、GitLab CI、Docker 多阶段构建、Terraform、Pulumi、部署策略、密钥管理、GitOps 和流水线安全。

事件驱动架构指南:Kafka、RabbitMQ、事件溯源、CQRS 与 Saga 模式

完整的事件驱动架构指南,涵盖 Apache Kafka、RabbitMQ、事件溯源、CQRS、Saga 模式、领域事件、异步消息模式、Schema 演进、无服务器事件处理、流处理和监控。

Redis完整指南:缓存、发布订阅、流和生产模式

掌握Redis的完整指南。含数据类型、Node.js ioredis、缓存模式、会话存储、发布订阅、流、Python redis-py、速率限制、事务和生产环境配置。