第 7 章

Lambda 无服务器实战

⏱️ 45 分钟
📝 20 题练习
备考助手

📌 核心知识点

函数设计模式冷启动优化并发控制成本优化

Lambda 无服务器实战

📖 Lambda 基础知识

AWS Lambda 是 AWS 的无服务器计算服务,让用户无需管理服务器即可运行代码。Lambda 是 AWS 无服务器架构的核心服务,与 API Gateway、DynamoDB、AWS Batch 配合使用。

📒 官方文档开发者指南FAQ定价

🖼️ Lambda 图解补充

ALB 组件架构(AWS 官方)

看图重点:Lambda 可作为 ALB 目标,入口流量治理和函数并发要联动。

Classic Load Balancer 架构(AWS 官方)

看图重点:旧架构迁移时常会从 CLB 迁到 ALB + Lambda 或容器。

ECS 分层架构(AWS 官方)

看图重点:Lambda 与容器常并存,按延迟与运维复杂度选择执行载体。

IAM 策略生效逻辑(AWS 官方)

看图重点:函数执行失败且访问 AWS 服务被拒时,先查角色策略链路。

VPC 基础网络架构(AWS 官方)

看图重点:Lambda 进 VPC 后网络路径更复杂,冷启动与连通性都要关注。

AWS 数据传输费用

看图重点:函数频繁跨区调用会放大传输费用,架构上应尽量就近部署。

成本分配标签示例(AWS 官方)

看图重点:函数数量增长后要靠标签做精细成本治理。

无服务器的理念

"无服务器"意味着用户不需要管理物理机器的配置、扩展或维护。Lambda 执行用户代码的机器被抽象为"容器"。

触发器 → Lambda 函数 → 目标服务
  │           │            │
事件源    执行代码    输出结果
(API GW,   (Node.js,   (DynamoDB,
 S3, SQS)  Python等)   S3, SNS)

支持的运行时

运行时版本冷启动时间适用场景
Node.js18.x, 20.x~200msAPI、数据处理
Python3.9-3.12~200ms数据科学、自动化
Java11, 17, 21~1-3s企业应用
Go1.x~100ms高性能
.NET6, 8~500msC# 应用
Ruby3.2~300ms脚本任务

内存与 CPU 的关系

# 重要:更多内存 = 更多 CPU 能力!

# Lambda 内存配置直接影响 CPU:
128 MB  → 最小 CPU 份额
1792 MB → 1 个完整 vCPU
10240 MB → 6 个 vCPU

# 所以即使不需要更多内存,
# 增加内存配置也可以加速 CPU 密集型任务

Lambda Reserved Concurrency 行为(AWS 官方)

看图重点:Reserved Concurrency 可以给关键函数保底容量,也能限制“问题函数”抢占全部并发。

Lambda 并发指标监控(AWS 官方)

看图重点:并发打满时先看 ConcurrentExecutionsThrottles,再决定扩容或限流。

函数设计模式

单一职责

# ✅ 推荐: 每个函数做一件事
def process_order(event, context):
    order = parse_order(event)
    validate_order(order)
    save_order(order)
    return {"status": "success"}

# ❌ 不推荐: 函数过于复杂
def do_everything(event, context):
    # 处理订单、发送邮件、更新库存...
    pass

容器复用和缓存

# 💡 关键优化:利用容器复用

# 在 handler 外部初始化 (只在冷启动时执行一次)
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('my-table')

# 缓存应用数据
_cached_config = None

def get_config():
    global _cached_config
    if _cached_config is None:
        _cached_config = load_config_from_s3()
    return _cached_config

def handler(event, context):
    # 使用已初始化的连接和缓存
    config = get_config()
    response = table.get_item(Key={'id': event['id']})
    return response

虽然 AWS 不提供容器复用的硬性保证,但通常如果短时间内再次调用同一函数,Lambda 会复用已有的"热"容器。

事件驱动架构

用户请求 → API Gateway → Lambda (验证)
                            │
                            ▼
                         SQS 队列
                            │
                            ▼
                      Lambda (处理)
                            │
                            ▼
                       DynamoDB

扇出模式

# 主函数将任务分发给多个 worker
def main_handler(event, context):
    tasks = split_tasks(event)
    for task in tasks:
        lambda_client.invoke(
            FunctionName='worker-function',
            InvocationType='Event',  # 异步调用
            Payload=json.dumps(task)
        )

# 示例项目: aws-lambda-fanout
# https://github.com/awslabs/aws-lambda-fanout

定时任务 (类似 Cron)

# 使用 CloudWatch Events 定时触发 Lambda

# cron 表达式示例:
# 每天 UTC 时间 10:00 执行
aws events put-rule \
  --name "DailyTrigger" \
  --schedule-expression "cron(0 10 * * ? *)"

# 每 5 分钟执行
aws events put-rule \
  --name "Every5Minutes" \
  --schedule-expression "rate(5 minutes)"

冷启动优化

什么是冷启动?

冷启动流程:
下载代码 → 创建容器 → 初始化运行时 → 执行代码
  (~100ms)   (~200ms)     (~50ms)      (你的代码)

热启动流程:
执行代码 (直接使用已有容器,毫秒级)

冷启动性能在 2018-2019 年间有了显著改进,现在简单函数的冷启动通常在 200-500ms 范围内。

VPC 内 Lambda 冷启动改进

# 重大改进!
# 之前:VPC 内 Lambda 冷启动约 15 秒
# 现在:VPC 内 Lambda 冷启动 < 1 秒

# AWS 通过改进 ENI 分配机制实现了这一优化
# https://aws.amazon.com/blogs/compute/announcing-improved-vpc-networking-for-aws-lambda-functions/

优化策略

1. 使用 Provisioned Concurrency

# 最有效消除冷启动的方法(re:invent 2019 发布)
aws lambda put-provisioned-concurrency-config \
  --function-name my-function \
  --qualifier prod \
  --provisioned-concurrent-executions 10

# 预热的容器始终就绪
# 但会持续产生费用

2. 减少部署包大小

# 使用 Lambda Layer 分离依赖
aws lambda publish-layer-version \
  --layer-name my-dependencies \
  --zip-file fileb://layer.zip

# 对于 Java:使用 ProGuard 优化
# 或者在运行时将依赖加载到 /tmp

3. 选择轻量级运行时

# 冷启动时间排序(从快到慢):
1. Go         (~100ms)
2. Python     (~200ms)
3. Node.js    (~200ms)
4. .NET       (~500ms)
5. Java       (~1-3s)

# 对于延迟敏感的场景,优先考虑 Go 或 Python

4. 定期触发保持热容器

# 使用 CloudWatch Events 每 5 分钟触发一次
# 保持容器温暖

def handler(event, context):
    # 检查是否为预热触发
    if event.get('source') == 'aws.events':
        return {'statusCode': 200, 'body': 'warmup'}

    # 正常业务逻辑
    ...

并发控制

并发类型

账户并发限制: 1000 (可申请提升到数万)
    │
    ├── 函数 A: 预留并发 100
    │       └── 最多 100 个并发执行
    │
    ├── 函数 B: 预留并发 200
    │       └── 最多 200 个并发执行
    │
    └── 其他函数: 共享剩余 700

设置预留并发

# 预留并发确保函数始终有可用容量
aws lambda put-function-concurrency \
  --function-name my-function \
  --reserved-concurrent-executions 100

# 注意:预留并发会从账户总量中扣除

处理失败和重试

# 使用 Dead Letter Queue (DLQ) 处理失败的事件
# 配置 SQS 作为 DLQ

# 在 Lambda 配置中设置:
# DeadLetterConfig:
#   TargetArn: arn:aws:sqs:region:account:my-dlq

# 处理限流
import time

def handler(event, context):
    try:
        # 业务逻辑
        pass
    except ClientError as e:
        if e.response['Error']['Code'] == 'TooManyRequestsException':
            time.sleep(1)  # 退避
            raise  # 重新抛出让 Lambda 重试

监控和调试

CloudWatch 集成

# Lambda 自动与 CloudWatch 集成
# 提供运行时 logger

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
    logger.info(f"Processing event: {event}")
    # ...

X-Ray 追踪

# Lambda 原生支持 AWS X-Ray(可选启用)
# 帮助诊断调用其他 AWS 服务时的问题

# 提供详细的调用图可视化
# 特别适合排查复杂的服务调用链

成本优化

计费模型

费用 = 请求次数 × 单价 + 执行时间(GB-秒) × 单价

示例:
- 100万次请求/月
- 每次执行 200ms, 256MB 内存

费用 = 1M × $0.20/M + 1M × 0.2s × 0.25GB × $0.0000166667
    = $0.20 + $0.83
    = $1.03/月

使用 ARM64 (Graviton2)

# Graviton2 处理器便宜 20%,且通常更快
aws lambda create-function \
  --function-name my-function \
  --architectures arm64 \
  ...

# 确保代码和依赖兼容 ARM

Lambda Power Tuning

# 使用 AWS Lambda Power Tuning 工具
# 找到最佳内存配置

# https://github.com/alexcasalboni/aws-lambda-power-tuning

# 它会用不同内存配置测试你的函数
# 生成性能/成本曲线,帮你做最优选择

无服务器框架

AWS SAM (Serverless Application Model)

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

Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9
      Events:
        Api:
          Type: Api
          Properties:
            Path: /hello
            Method: GET
# 本地测试
sam local invoke MyFunction

# 部署
sam deploy --guided

Serverless Framework

# serverless.yml
service: my-service

provider:
  name: aws
  runtime: python3.9

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
# 部署
serverless deploy

其他选择

  • Chalice - AWS 官方 Python 框架
  • Zappa - Django/Flask 部署到 Lambda
  • OpenFaaS - Kubernetes 上的无服务器

API Gateway 集成

基础设置

API Gateway
├── 提供 RESTful API 前端
├── 自动扩展
├── 支持 OpenAPI/Swagger
├── 内置 CloudWatch 日志
└── 与 Cognito 集成认证

认证选项

# 1. Amazon Cognito(推荐大多数场景)
# 最简单的用户认证方式

# 2. 自定义授权器
# 自己写 Lambda 判断请求是否允许

# 3. API Key
# 简单的 API 访问控制

REST vs RPC 风格

# API Gateway 很适合 REST,但 RPC 风格也很好用

# REST 风格
GET /users/123
POST /users

# RPC 风格(适合内部服务)
POST /getUser
POST /createUser

# RPC 对于深层服务可能更简单清晰

⚠️ Lambda 常见陷阱

1. 资源限制

# 重要限制(2024年):
请求/响应负载: 6 MB(同步)
部署包 (zip): 50 MB(压缩后)
代码/依赖: 250 MB(解压后)
/tmp 临时存储: 10 GB
执行超时: 最大 15 分钟

# Java 部署包特别容易超限
# 考虑使用 ProGuard 或运行时加载依赖到 /tmp

2. VPC 相关问题

# 🔸 VPC 内 Lambda 需要 NAT Gateway 访问外网
# 即使在公有子网也不会分配公有 IP!

# 🔸 ENI 数量限制
# 每个并发 Lambda 需要一个 ENI
# 初始限制约 350,可申请提升到 1000+

# 🔸 访问 AWS 服务用 VPC 端点
# 避免通过 NAT Gateway
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxx \
  --service-name com.amazonaws.ap-southeast-2.s3 \
  ...

3. 测试挑战

# 🔸 本地测试 Lambda 是个挑战

# 官方工具:SAM Local
sam local invoke MyFunction -e event.json
sam local start-api

# 也可以用 LocalStack 模拟 AWS 服务

4. 版本和别名管理

# 🔸 AWS 官方的版本/别名管理流程很痛苦

# 建议:在 Lambda 外部管理部署流程
# 例如:每个环境一个 AWS 账户
#      只需关心最新版本
#      回滚由外部工具处理

5. S3 触发器问题

# 🔸 添加/删除 S3 触发器时可能报错:
# "Configuration is ambiguously defined..."

# 原因:两个规则的前缀/后缀重叠

# 解决:在 S3 控制台的 Events 标签手动删除旧触发器

6. DynamoDB 触发器问题

# 🔸 可能报错:
# "internal Lambda error. Please contact Lambda customer support."

# 通常是因为 DynamoDB Stream 48 小时内没有数据
# 解决:删除并重新创建触发器

7. API Gateway 限制

# 🔸 只支持 HTTPS,不支持 HTTP(这其实是好事)

# 🔸 不支持多区域部署用于高可用
# 虽然是从边缘位置提供服务,但是单区域

# 🔸 集成超时无法提高
# Lambda: 29 秒
# HTTP: 29 秒

# 🔸 请求/响应过大会被 CloudWatch 截断
# 完整日志需要在 Lambda 中实现

Lambda 替代方案

平台服务名
Google CloudCloud Functions
AzureAzure Functions
IBMOpenWhisk
KubernetesOpenFaaS, Knative

📚 本章小结

  • Lambda 是无服务器架构的核心,与 API Gateway、DynamoDB 配合使用
  • 内存配置影响 CPU,增加内存可以加速计算
  • 利用容器复用和初始化代码优化减少冷启动影响
  • VPC 内 Lambda 冷启动已大幅改进(< 1秒)
  • Provisioned Concurrency 可以完全消除冷启动
  • 使用 DLQ 处理失败事件
  • X-Ray 帮助诊断复杂调用链
  • ARM64 (Graviton2) 便宜 20% 且通常更快
  • 注意资源限制:6MB 负载、50MB 部署包、10GB /tmp
  • VPC 内 Lambda 需要 NAT Gateway 或 VPC 端点访问外部服务
📝 章节练习 (20 题)
开始练习