AWS 无服务器架构

aws-serverless
分类通用
作者Agentic Awesome Skills 社区
许可MIT
评分4.60/5
使用14.1K

AWS Serverless

构建 AWS 生产级无服务器应用的专业技能。涵盖 Lambda 函数、API Gateway、DynamoDB、SQS/SNS 事件驱动模式、SAM/CDK 部署以及冷启动优化。

原则

  • 合理配置内存和超时时间(先测量后优化)
  • 针对延迟敏感型工作负载尽量减少冷启动
  • 对 Java/.NET 函数使用 SnapStart
  • 简单场景优先选择 HTTP API 而非 REST API
  • 通过 DLQ(死信队列)和重试机制设计容错方案
  • 保持部署包体积精简
  • 使用环境变量进行配置
  • 实现带有关联 ID(Correlation ID)的结构化日志

模式

Lambda Handler 模式

包含错误处理的规范 Lambda 函数结构

适用场景:任何 Lambda 函数实现、API 处理程序、事件处理器、定时任务

javascript
// Node.js Lambda Handler
// handler.js

// 在 handler 外部初始化(在多次调用间复用)
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

// Handler 函数
exports.handler = async (event, context) => {
// 可选:不等待事件循环清空 (Node.js)
context.callbackWaitsForEmptyEventLoop = false;

try {
// 根据事件源解析输入
const body = typeof event.body === 'string'
? JSON.parse(event.body)
: event.body;

// 业务逻辑
const result = await processRequest(body);

// 返回兼容 API Gateway 的响应
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(result)
};
} catch (error) {
console.error('Error:', JSON.stringify({
error: error.message,
stack: error.stack,
requestId: context.awsRequestId
}));

return {
statusCode: error.statusCode || 500,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
error: error.message || 'Internal server error'
})
};
}
};

async function processRequest(data) {
// 此处编写业务逻辑
const result = await docClient.send(new GetCommand({
TableName: process.env.TABLE_NAME,
Key: { id: data.id }
}));
return result.Item;
}

python
# Python Lambda Handler

handler.py

import json
import os
import logging
import boto3
from botocore.exceptions import ClientError

在 handler 外部初始化(在多次调用间复用)

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

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['TABLE_NAME'])

def handler(event, context):
try:
# 解析输入
body = json.loads(event.get('body', '{}')) if isinstance(event.get('body'), str) else event.get('body', {})

# 业务逻辑
result = process_request(body)

return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'Access-Contr


ol-Allow-Origin': '*'
},
'body': json.dumps(result)
}

except ClientError as e:
logger.error(f"DynamoDB error: {e.response['Error']['Message']}")
return error_response(500, 'Database error')

except json.JSONDecodeError:
return error_response(400, 'Invalid JSON')

except Exception as e:
logger.error(f"Unexpected error: {str(e)}", exc_info=True)
return error_response(500, 'Internal server error')

def process_request(data):
response = table.get_item(Key={'id': data['id']})
return response.get('Item')

def error_response(status_code, message):
return {
'statusCode': status_code,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'error': message})
}

code
### 最佳实践

  • 在 handler 外部初始化客户端(可在热启动调用中复用)
  • 始终返回标准的 API Gateway 响应格式
  • 使用结构化 JSON 日志以便于 CloudWatch Insights 分析
  • 在错误日志中包含请求 ID 以便追踪

API Gateway 集成模式

REST API 和 HTTP API 与 Lambda 的集成

适用场景:构建由 Lambda 支持的 REST API,或需要为函数提供 HTTP 端点

yaml

template.yaml (SAM)


AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
Function:
Runtime: nodejs20.x
Timeout: 30
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ItemsTable

Resources:
# HTTP API (推荐用于简单场景)
HttpApi:
Type: AWS::Serverless::HttpApi
Properties:
StageName: prod
CorsConfiguration:
AllowOrigins:
- "*"
AllowMethods:
- GET
- POST
- DELETE
AllowHeaders:
- "*"

# Lambda 函数
GetItemFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/get.handler
Events:
GetItem:
Type: HttpApi
Properties:
ApiId: !Ref HttpApi
Path: /items/{id}
Method: GET
Policies:
- DynamoDBReadPolicy:
TableName: !Ref ItemsTable

CreateItemFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/create.handler
Events:
CreateItem:
Type: HttpApi
Properties:
ApiId: !Ref HttpApi
Path: /items
Method: POST
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref ItemsTable

# DynamoDB 表
ItemsTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST

Outputs:
ApiUrl:
Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com/prod"

code
javascript
// src/handlers/get.js
const { getItem } = require('../lib/dynamodb');

exports.handler = async (event) => {
const id = event.pathParameters?.id;

if (!id) {
return {
statusCode: 400,
body: JSON.stringify({ error: 'Missing id parameter' })
};
}

const item = await getItem(id);

if (!item) {
return {
statusCode: 404,
body: JSON.stringify({ error: 'Item not found' })
};
}

return {
statusCode: 200,
body: JSON.stringify(item)
};
};

code
### 目录结构

project/
├── template.yaml # SAM 模板
├── src
/
│ ├── handlers/
│ │ ├── get.js
│ │ ├── create.js
│ │ └── delete.js
│ └── lib/
│ └── dynamodb.js
└── events/
└── event.json # 测试事件

Api_comparison (API 对比)

  • Http_api:
- 低延迟 (~10ms) - 低成本 (便宜 50-70%) - 更简单,功能较少 - 适用场景:大多数 REST API
  • Rest_api:
- 功能更丰富 (缓存、请求验证、WAF) - 支持使用计划 (Usage plans) 和 API 密钥 - 支持请求/响应转换 - 适用场景:复杂 API、企业级功能

Event-Driven SQS Pattern (SQS 事件驱动模式)

由 SQS 触发 Lambda 以实现可靠的异步处理

适用场景:解耦、异步处理、需要重试逻辑和死信队列 (DLQ)、批量处理消息

yaml

template.yaml


Resources:
ProcessorFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/processor.handler
Events:
SQSEvent:
Type: SQS
Properties:
Queue: !GetAtt ProcessingQueue.Arn
BatchSize: 10
FunctionResponseTypes:
- ReportBatchItemFailures # 处理部分批次失败

ProcessingQueue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 180 # Lambda 超时时间的 6 倍
RedrivePolicy:
deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn
maxReceiveCount: 3

DeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
MessageRetentionPeriod: 1209600 # 14 天

code
javascript
// src/handlers/processor.js
exports.handler = async (event) => {
const batchItemFailures = [];

for (const record of event.Records) {
try {
const body = JSON.parse(record.body);
await processMessage(body);
} catch (error) {
console.error(Failed to process message ${record.messageId}:, error);
// 将此项标记为失败(将被重试)
batchItemFailures.push({
itemIdentifier: record.messageId
});
}
}

// 返回失败项以进行重试
return { batchItemFailures };
};

async function processMessage(message) {
// 处理逻辑
console.log('Processing:', message);

// 模拟工作
await saveToDatabase(message);
}

code
python

Python version


import json
import logging

logger = logging.getLogger()

def handler(event, context):
batch_item_failures = []

for record in event['Records']:
try:
body = json.loads(record['body'])
process_message(body)
except Exception as e:
logger.error(f"Failed to process {record['messageId']}: {e}")
batch_item_failures.append({
'itemIdentifier': record['messageId']
})

return {'batchItemFailures': batch_item_failures}

code
### Best_practices (最佳实践)

  • 将 VisibilityTimeout 设置为 Lambda 超时时间的 6 倍
  • 使用 ReportBatchItemFailures 处理部分批次失败
  • 为“毒药消息” (poison messages) 始终配置 DLQ
  • 确保消息处理具有幂等性

DynamoDB Streams Pattern (DynamoDB Streams 模式)

使用 Lambda 对 DynamoDB 表的变更做出响应

适用场景:对数据变更的实时响应、跨区域复制、审计日志、通知

yaml

template.yaml


Resources:
ItemsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: items
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES

StreamProcessorFunction:
Typ

code
e: AWS::Serverless::Function
Properties:
Handler: src/handlers/stream.handler
Events:
Stream:
Type: DynamoDB
Properties:
Stream: !GetAtt ItemsTable.StreamArn
StartingPosition: TRIM_HORIZON
BatchSize: 100
MaximumRetryAttempts: 3
DestinationConfig:
OnFailure:
Destination: !GetAtt StreamDLQ.Arn

StreamDLQ:
Type: AWS::SQS::Queue

javascript
// src/handlers/stream.js
exports.handler = async (event) => {
  for (const record of event.Records) {
    const eventName = record.eventName;  // INSERT, MODIFY, REMOVE

// 将 DynamoDB 格式反序列化为普通 JS 对象
const newImage = record.dynamodb.NewImage
? unmarshall(record.dynamodb.NewImage)
: null;
const oldImage = record.dynamodb.OldImage
? unmarshall(record.dynamodb.OldImage)
: null;

console.log(${eventName}: , { newImage, oldImage });

switch (eventName) {
case 'INSERT':
await handleInsert(newImage);
break;
case 'MODIFY':
await handleModify(oldImage, newImage);
break;
case 'REMOVE':
await handleRemove(oldImage);
break;
}
}
};

// 使用 AWS SDK v3 的 unmarshall
const { unmarshall } = require('@aws-sdk/util-dynamodb');

Stream_view_types (流视图类型)

  • KEYS_ONLY: 仅主键属性
  • NEW_IMAGE: 修改后状态
  • OLD_IMAGE: 修改前状态
  • NEW_AND_OLD_IMAGES: 修改前后的状态

冷启动优化模式

降低 Lambda 冷启动延迟

适用场景:延迟敏感型应用、面向用户的 API、高流量函数

1. 优化包体积

javascript
// 使用模块化的 AWS SDK v3 导入
// 推荐 - 仅导入所需模块
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');

// 不推荐 - 导入整个 SDK
const AWS = require('aws-sdk'); // 避免这样做!

2. 使用 SnapStart (Java/.NET)

yaml
# template.yaml
Resources:
  JavaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.example.Handler::handleRequest
      Runtime: java21
      SnapStart:
        ApplyOn: PublishedVersions  # 启用 SnapStart
      AutoPublishAlias: live

3. 合理配置内存

yaml
# 内存越多 = CPU 越多 = 初始化越快
Resources:
  FastFunction:
    Type: AWS::Serverless::Function
    Properties:
      MemorySize: 1024  # 1GB 可获得完整的 vCPU
      Timeout: 30

4. 预留并发 (Provisioned Concurrency)

yaml
Resources:
  CriticalFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/critical.handler
      AutoPublishAlias: live

ProvisionedConcurrency:
Type: AWS::Lambda::ProvisionedConcurrencyConfig
Properties:
FunctionName: !Ref CriticalFunction
Qualifier: live
ProvisionedConcurrentExecutions: 5

5. 保持初始化轻量化

python
# 推荐 - 延迟初始化
_table = None

def get_table():
global _table
if _table is None:
dynamodb = boto3.resource('dynamodb')
_table = dynamodb.Table(os.environ['TABLE_NAME'])
return _table

def handler(event, context):
table = get_table() # 仅在首次使用时初始化
# ...

优化优先级

  • 1: 减小包体积 (影响最大)
  • 2: 为 Java/.NET 使用 SnapStart
  • 3: 增加内存以加快初始化
  • 4: 延迟重量级模块的导入
  • 5: 预留并发
并发限制(最后手段)

SAM 本地开发模式

使用 SAM CLI 进行本地测试和调试

适用场景:本地开发与测试、调试 Lambda 函数、在本地测试 API Gateway

bash
# 安装 SAM CLI
pip install aws-sam-cli

初始化新项目

sam init --runtime nodejs20.x --name my-api

构建项目

sam build

本地运行

sam local start-api

调用单个函数

sam local invoke GetItemFunction --event events/get.json

本地调试 (Node.js 配合 VS Code)

sam local invoke --debug-port 5858 GetItemFunction

部署

sam deploy --guided
json
// events/get.json (测试事件)
{
  "pathParameters": {
    "id": "123"
  },
  "httpMethod": "GET",
  "path": "/items/123"
}
json
// .vscode/launch.json (用于调试)
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach to SAM CLI",
      "type": "node",
      "request": "attach",
      "address": "localhost",
      "port": 5858,
      "localRoot": "${workspaceRoot}/src",
      "remoteRoot": "/var/task/src",
      "protocol": "inspector"
    }
  ]
}

常用命令

  • Sam_build: 构建 Lambda 部署包
  • Sam_local_start_api: 启动本地 API Gateway
  • Sam_local_invoke: 调用单个函数
  • Sam_deploy: 部署到 AWS
  • Sam_logs: 查看 CloudWatch 日志实时流

CDK Serverless 模式

使用 AWS CDK 实现基础设施即代码 (IaC)

适用场景:涉及 Lambda 之外的复杂基础设施、倾向于使用编程语言而非 YAML、需要可复用的构建块 (Constructs)

typescript
// lib/api-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { Construct } from 'constructs';

export class ApiStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// DynamoDB 表
const table = new dynamodb.Table(this, 'ItemsTable', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY, // 仅用于开发环境
});

// Lambda 函数
const getItemFn = new lambda.Function(this, 'GetItemFunction', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'get.handler',
code: lambda.Code.fromAsset('src/handlers'),
environment: {
TABLE_NAME: table.tableName,
},
memorySize: 256,
timeout: cdk.Duration.seconds(30),
});

// 授予权限
table.grantReadData(getItemFn);

// API Gateway
const api = new apigateway.RestApi(this, 'ItemsApi', {
restApiName: 'Items Service',
defaultCorsPreflightOptions: {
allowOrigins: apigateway.Cors.ALL_ORIGINS,
allowMethods: apigateway.Cors.ALL_METHODS,
},
});

const items = api.root.addResource('items');
const item = items.addResource('{id}');

item.addMethod('GET', new apigateway.LambdaIntegration(getItemFn));

// 输出 API URL
new cdk.CfnOutput(this, 'ApiUrl', {
value: api.url,
});
}
}

bash
# CDK 命令
npm install -g aws-cdk
cdk init app --language typescript
cdk synth    # 生成 CloudFormation 模板
cdk diff     # 显示变更差异
cdk deploy   # 部署到 AWS

潜在坑点 (Sharp Edges)

冷启动 INIT 阶段现已计费 (2025年8月)

严重程度:高

场景:在生产环境中运行 Lambda 函数

症状:
无法解释的费用增加
Lambda 成本增加(高出 10-50%)。
账单包含函数初始化费用。
启动逻辑较重的函数成本高于预期。

原因:
自 2025 年 8 月 1 日起,AWS 对 INIT 阶段的计费方式与调用时长一致。此前,冷启动初始化并不按全时长计费。

受影响的函数特征:

  • 依赖加载过重(包体积大)

  • 初始化代码运行缓慢

  • 冷启动频繁(流量低或并发能力差)

现在,冷启动不仅影响延迟,还直接影响账单。

建议修复方案:

测量 INIT 阶段

bash
# 在 CloudWatch Logs 中检查 INIT_REPORT

查看 Init Duration(毫秒)

日志示例:

INIT_REPORT Init Duration: 423.45 ms

缩短 INIT 时长

javascript
// 1. 最小化包体积
// 使用 tree shaking,排除开发依赖
// npm prune --production

// 2. 延迟加载重型依赖
let heavyLib = null;
function getHeavyLib() {
if (!heavyLib) {
heavyLib = require('heavy-library');
}
return heavyLib;
}

// 3. 使用 AWS SDK v3 的模块化导入
const { S3Client } = require('@aws-sdk/client-s3');
// 不要使用: const AWS = require('aws-sdk');

为 Java/.NET 使用 SnapStart

yaml
Resources:
  JavaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: java21
      SnapStart:
        ApplyOn: PublishedVersions

监控冷启动频率

javascript
// 使用自定义指标追踪冷启动
let isColdStart = true;

exports.handler = async (event) => {
if (isColdStart) {
console.log('COLD_START');
// 在此处添加 CloudWatch 自定义指标
isColdStart = false;
}
// ...
};

Lambda 超时配置错误

严重程度:高

场景:运行 Lambda 函数,尤其是涉及外部调用时

症状:

  • 函数意外超时。

  • 日志中出现 "Task timed out after X seconds"。

  • 处理部分完成但无响应。

  • 静默失败且未捕获错误。

原因:
Lambda 的默认超时时间仅为 3 秒,最大可设为 15 分钟。

常见超时原因:

  • 默认超时时间对于工作负载过短

  • 下游服务响应时间超出预期

  • VPC 网络问题

  • 死循环或阻塞操作

  • S3 下载的文件比预期大

Lambda 在超时时会直接终止,不会进行优雅关闭。

建议修复方案:

设置合理的超时时间

yaml
# template.yaml
Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Timeout: 30  # 秒 (最大 900)
      # 设置为:预期时长 + 缓冲时间

实现超时感知

javascript
exports.handler = async (event, context) => {
  // 获取剩余时间
  const remainingTime = context.getRemainingTimeInMillis();

// 如果时间不足,优雅地报错
if (remainingTime < 5000) {
console.warn('Running low on time, aborting');
throw new Error('Insufficient time remaining');
}

// 对于耗时操作,定期检查
for (const item of items) {
if (context.getRemainingTimeInMillis() < 10000) {
// 保存进度并优雅退出
await saveProgress(processedItems);
throw new Error('Timeout approaching, saved progress');
}
await processItem(item);
}
};

设置下游调用超时

javascript
const axios = require('axios');

// 为 HTTP 调用始终设置超时
const response = await axios.get('https://api.example.com/data', {
timeout: 5000 // 5 秒
});

内存溢出 (OOM) 崩溃

严重程度:高

场景:
场景:Lambda 函数处理数据

症状:

  • 函数突然停止且无错误提示。

  • CloudWatch 日志出现截断。

  • “Max Memory Used” 达到配置上限。

  • 高负载下行为不一致。

故障原因:
当 Lambda 超过内存分配时,AWS 会强制终止运行时。此过程不会抛出可捕获的异常。

常见原因:

  • 在内存中处理大文件

  • 跨调用出现内存泄漏

  • 缓冲整个响应体

  • 重型库占用过多内存

推荐修复方案:

增加内存分配

yaml
Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      MemorySize: 1024  # MB (128-10240)
      # 内存越多 = CPU 性能越强

流式处理大数据

javascript
// 错误做法 - 将整个文件加载到内存
const data = await s3.getObject(params).promise();
const content = data.Body.toString();

// 正确做法 - 流式处理
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
const s3 = new S3Client({});

const response = await s3.send(new GetObjectCommand(params));
const stream = response.Body;

// 分块处理流
for await (const chunk of stream) {
await processChunk(chunk);
}

监控内存使用情况

javascript
exports.handler = async (event, context) => {
  const used = process.memoryUsage();
  console.log('Memory:', {
    heapUsed: Math.round(used.heapUsed / 1024 / 1024) + 'MB',
    heapTotal: Math.round(used.heapTotal / 1024 / 1024) + 'MB'
  });
  // ...
};

使用 Lambda Power Tuning

bash
# 寻找最佳内存设置

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

VPC 绑定 Lambda 冷启动延迟

严重程度:中

场景:在 VPC 中访问私有资源的 Lambda 函数

症状:

  • 冷启动极其缓慢(过去需 10 秒以上,现在约 100 毫秒)。

  • 空闲期后的首次调用出现超时。

  • 函数在 VPC 中可运行,但比非 VPC 环境慢。

故障原因:
VPC 中的 Lambda 函数需要弹性网络接口 (ENI)。虽然 AWS 通过 Hyperplane ENI 显著改善了这一点,但仍存在以下问题:

  • VPC 中的首次冷启动仍有开销
  • NAT 网关问题可能导致超时
  • 安全组配置错误导致流量被拦截
  • DNS 解析可能缓慢

推荐修复方案:

验证 VPC 配置

yaml
Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      VpcConfig:
        SecurityGroupIds:
          - !Ref LambdaSecurityGroup
        SubnetIds:
          - !Ref PrivateSubnet1
          - !Ref PrivateSubnet2  # 跨多个可用区 (AZ)

LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Lambda SG
VpcId: !Ref VPC
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0 # 允许 HTTPS 出站流量

为 AWS 服务使用 VPC 终端节点 (Endpoints)

yaml
# 避免调用 AWS 服务时经过 NAT 网关
DynamoDBEndpoint:
  Type: AWS::EC2::VPCEndpoint
  Properties:
    ServiceName: !Sub com.amazonaws.${AWS::Region}.dynamodb
    VpcId: !Ref VPC
    RouteTableIds:
      - !Ref PrivateRouteTable
    VpcEndpointType: Gateway

S3Endpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
ServiceName: !Sub com.amazonaws.${AWS::Region}.s3
VpcId: !Ref VPC
VpcEndpointType: Gateway

仅在必要时使用 VPC

除非需要以下项,否则不要将 Lambda 绑定到 VPC:

  • 访问 VPC 内的 RDS/ElastiCache

  • 访问私有 EC2 实例

  • 合规性要求

大多数 AWS 服务都可以通过...
无需 VPC 访问。

Node.js 事件循环未清除

严重程度:中

场景:包含回调或定时器的 Node.js Lambda 函数

症状:

  • 函数运行至超时时间才返回。

  • 即使逻辑已完成,仍提示 "Task timed out"。

  • 产生额外的空闲时间计费。

原因:
默认情况下,Lambda 在返回前会等待 Node.js 事件循环清空。如果你有:

  • 未解决的 setTimeout/setInterval

  • 悬挂的数据库连接

  • 待处理的回调

即使响应已准备好,Lambda 仍会等待直到超时。

推荐修复方案:

告知 Lambda 不要等待事件循环

javascript
exports.handler = async (event, context) => {
  // 不要等待事件循环清空
  context.callbackWaitsForEmptyEventLoop = false;

// 业务代码
const result = await processRequest(event);

return {
statusCode: 200,
body: JSON.stringify(result)
};
};

正确关闭连接

javascript
// 对于数据库连接,请使用连接池或显式关闭连接

const mysql = require('mysql2/promise');

exports.handler = async (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;

const connection = await mysql.createConnection({...});
try {
const [rows] = await connection.query('SELECT * FROM users');
return { statusCode: 200, body: JSON.stringify(rows) };
} finally {
await connection.end(); // 务必关闭
}
};

API Gateway 负载大小限制

严重程度:中

场景:返回大型响应或接收大型请求

症状:

  • 出现 "413 Request Entity Too Large" 错误

  • "Execution failed due to configuration error: Malformed Lambda proxy response"

  • 响应被截断或失败

原因:
API Gateway 有严格的负载限制:

  • REST API:请求/响应 10 MB

  • HTTP API:请求/响应 10 MB

  • Lambda 本身:同步响应 6 MB,异步 256 KB

超过这些限制会导致不明显的失败。

推荐修复方案:

针对大文件上传

javascript
// 使用 S3 预签名 URL,而不是通过 API Gateway 传输

const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');

exports.handler = async (event) => {
const s3 = new S3Client({});

const command = new PutObjectCommand({
Bucket: process.env.BUCKET_NAME,
Key: uploads/${Date.now()}.file
});

const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 300 });

return {
statusCode: 200,
body: JSON.stringify({ uploadUrl })
};
};

针对大型响应

javascript
// 存储在 S3 中,返回预签名的下载 URL
exports.handler = async (event) => {
  const largeData = await generateLargeReport();

await s3.send(new PutObjectCommand({
Bucket: process.env.BUCKET_NAME,
Key: reports/${reportId}.json,
Body: JSON.stringify(largeData)
}));

const downloadUrl = await getSignedUrl(s3,
new GetObjectCommand({
Bucket: process.env.BUCKET_NAME,
Key: reports/${reportId}.json
}),
{ expiresIn: 3600 }
);

return {
statusCode: 200,
body: JSON.stringify({ downloadUrl })
};
};

死循环或递归调用

严重程度:高

场景:由事件触发的 Lambda

症状:

  • 成本失控。

  • 几分钟内产生数千次调用。

  • CloudWatch 日志显示重复调用。

  • Lambda 写入触发自身的源存储桶/表中。

原因:
Lambda 可能会意外触发自身:

  • S3 触发器写回同一个存储桶

  • DynamoDB 触发器更新同一张表

  • SNS 发布到触发自身的 Topic

  • Step Functions 错误处理不当

建议修复方案:

使用不同的存储桶/前缀

yaml
# 带有前缀过滤的 S3 触发器
Events:
  S3Event:
    Type: S3
    Properties:
      Bucket: !Ref InputBucket
      Events: s3:ObjectCreated:*
      Filter:
        S3Key:
          Rules:
            - Name: prefix
              Value: uploads/  # 仅在 uploads/ 目录下触发

输出到不同的存储桶或前缀

OutputBucket 或 processed/ 前缀

添加幂等性检查

javascript
exports.handler = async (event) => {
  for (const record of event.Records) {
    const key = record.s3.object.key;

// 如果是已处理的文件则跳过
if (key.startsWith('processed/')) {
console.log('Skipping already processed file:', key);
continue;
}

// 处理并写入到不同位置
await processFile(key);
await writeToS3(processed/${key}, result);
}
};

设置预留并发作为熔断机制

yaml
Resources:
  RiskyFunction:
    Type: AWS::Serverless::Function
    Properties:
      ReservedConcurrentExecutions: 10  # 最大 10 个并行实例
      # 限制失控调用带来的影响范围

使用 CloudWatch 警报进行监控

yaml
InvocationAlarm:
  Type: AWS::CloudWatch::Alarm
  Properties:
    MetricName: Invocations
    Namespace: AWS/Lambda
    Statistic: Sum
    Period: 60
    EvaluationPeriods: 1
    Threshold: 1000  # 每分钟调用数 >1000 时报警
    ComparisonOperator: GreaterThanThreshold

验证检查项

硬编码 AWS 凭证

严重程度:ERROR AWS 凭证绝不能硬编码。 提示信息:检测到硬编码的 AWS Access Key。请使用 IAM 角色或环境变量。

源代码中包含 AWS Secret Key

严重程度:ERROR Secret Key 应使用 Secrets Manager 或环境变量。 提示信息:硬编码的 AWS Secret Key。请使用 IAM 角色或 Secrets Manager。

IAM 策略权限过大

严重程度:WARNING 避免在 Lambda IAM 角色中使用通配符权限。 提示信息:IAM 策略权限过大。请遵循最小权限原则。

Lambda 处理函数缺少错误处理

严重程度:WARNING Lambda 处理函数应包含 try/catch 以实现优雅的错误处理。 提示信息:Lambda 处理函数缺少错误处理。请添加 try/catch。

缺失 callbackWaitsForEmptyEventLoop 配置

严重程度:INFO Node.js 处理函数应设置 callbackWaitsForEmptyEventLoop。 提示信息:建议设置 context.callbackWaitsForEmptyEventLoop = false。

默认内存配置

严重程度:INFO 默认的 128MB 对于许多工作负载来说可能过低。 提示信息:正在使用默认的 128MB 内存。考虑增加内存以提升性能。

超时配置过低

严重程度:WARNING 过低的超时时间可能会导致意外失败。 提示信息:1-3 秒的超时时间可能过低。如果涉及外部调用,请增加超时时间。

未配置死信队列 (DLQ)

严重程度:WARNING 异步函数应配置 DLQ 以处理调用失败的情况。 提示信息:未配置 DLQ。请为异步调用添加 DLQ。

导入完整的 AWS SDK v2

严重程度:WARNING 从 AWS SDK v3 导入特定客户端以减小包体积。 提示信息:导入了完整的 AWS SDK。请使用模块化的 SDK v3 导入以减小包体积。

硬编码 DynamoDB 表名

严重程度:WARNING 表名应通过环境变量获取。 提示信息:硬编码的表名。请使用环境变量以提高可移植性。

协作

委派触发条件

  • 用户需要 GCP Serverless -> gcp-clou
  • d-run(容器使用 Cloud Run,事件使用 Cloud Functions)
  • 用户需要 Azure serverless $\rightarrow$ azure-functions(Azure Functions, Logic Apps)
  • 用户需要数据库设计 $\rightarrow$ postgres-wizard(RDS 设计,或使用 DynamoDB 模式)
  • 用户需要身份验证 $\rightarrow$ auth-specialist(Cognito, API Gateway 授权器)
  • 用户需要复杂工作流 $\rightarrow$ workflow-automation(Step Functions, EventBridge)
  • 用户需要 AI 集成 $\rightarrow$ llm-architect(Lambda 调用 Bedrock 或外部 LLM)

使用场景

当请求明确符合上述能力和模式时,请使用此技能。

局限性

  • 仅在任务明确符合上述范围时使用此技能。
  • 不要将输出视为针对特定环境的验证、测试或专家评审的替代方案。
  • 如果缺少必要的输入、权限、安全边界或成功标准,请停止并请求澄清。