Amazon SAA-C03: 无服务器与事件驱动架构 / API 集成 — 学习指南
属于 AWS SAA-C03 — 完整学习指南. 使用经过验证的答案练习: Amazon 考试中心, 或参加限时模拟考试: ExamRoll.io.
AWS Lambda:执行模型、运行时生命周期与冷启动
Lambda 在隔离的 Firecracker 微虚拟机 (micro-VM) 中执行代码,其生命周期遵循两个阶段。INIT 阶段负责置备容器、引导运行时、下载并解压部署包,以及运行任何模块级别的初始化代码——例如构建 SDK 客户端、加载 KMS 解密的密钥、设置数据库连接池、JVM 类加载和 JIT 预热。INVOKE 阶段则运行处理器函数 (handler)。冷启动会产生完整的 INIT 成本;而热调用则会复用该环境,因此,在处理器函数外部缓存的任何内容(如 HTTP keep-alive 连接池、已解密的密钥、数据库客户端)都可以在该容器上的多次调用之间保持不变。这就是为什么标准模式是在模块作用域内构造昂贵的对象并复用它们的原因:
import boto3, os
ddb = boto3.client('dynamodb') # reused across warm invokes
_secret = None
def _load_secret():
global _secret
if _secret is None:
_secret = kms.decrypt(...) # KMS call once per container, not per invoke
return _secret
def handler(event, ctx):
...
冷启动的严重程度因运行时而异。Node.js 和 Python 通常是几十到几百毫秒;而 JVM 和 .NET 可能会超过一秒。对于附加到 VPC 的函数,Hyperplane ENI 已在很大程度上分摊了过去附加 ENI 所带来的性能开销,但部署包大小和繁重的 INIT 代码仍然是主要影响因素。
有三种工具可以从不同角度解决冷启动问题:
| 功能 | 行为 | 成本 | 最佳适用场景 |
|---|---|---|---|
| 按需并发 | 默认模式;通过约 500–3,000 的初始突发,然后每分钟增加 500 个进行扩展 | 按调用次数 + 持续时间计费 | 突发性、对延迟不敏感的负载 |
| 预置并发 | 预先初始化 N 个环境,在流量到达前完成 INIT 阶段 | 为预置的单元支付 24/7 费用 + 调用费用 | 有严格 p99 延迟 SLA 的场景 |
| SnapStart (Java, Python, .NET) | 在 INIT 后创建 Firecracker 快照;在冷启动时恢复 | Java 无额外费用;其他运行时有少量缓存费用 | 完整预置并发会造成浪费的 Java 函数 |
对于没有严格延迟合同的 Java 工作负载,SnapStart 是最具成本效益的理想选择——它能将冷启动时间降低大约一个数量级,且没有每次调用的附加费。当同步 API 必须在突发期间将 p99 延迟保持在(例如)100 毫秒以下时,预置并发是正确的答案;将其大小设置为 p95 突发水平可以弥补尾部延迟的差距。预置数量不足是一个典型的失败模式:按需模式只有在请求到达时才会启动新容器,因此流量尖峰会导致真实用户经历数秒的等待。
# SAM: SnapStart on a Java 17 function
MyJavaFn:
Type: AWS::Serverless::Function
Properties:
Runtime: java17
SnapStart: { ApplyOn: PublishedVersions }
AutoPublishAlias: live
预置并发本身可以通过 Application Auto Scaling 进行扩展——例如,一个计划操作在 07:45 将并发从 10 增加到 200,在 10:00 再降回去,这样既避免了为夜间的热容量付费,又消除了早高峰的冷启动。
内存大小同时也是一个 CPU 调节器:vCPU 数量会随着内存线性增加,直到大约每 vCPU 1,769 MB。一个固定在 128 MB 的函数,其总成本可能比 1,024 MB 的更高,因为翻倍的运行时间成本通常会超过翻倍的每毫秒单价。不要猜测——应使用 AWS Lambda Power Tuning 工具,针对有代表性的负载来测试各种配置。
Lambda 并发控制与下游保护
三种并发控制很重要,各自作用不同:
- 预留并发 限制并保留一个函数可以消耗的并发执行数量。它从账户的并发池中划分出一部分容量,并保护下游系统(如小型 RDS 实例、有速率限制的合作伙伴 API)不被压垮。
- 预置并发 预先初始化环境——这是一个延迟调节杠杆,而不是扩展杠杆。
- 未预留账户并发 是共享池,每个区域默认为 1,000。
认为 Lambda “无限扩展”的假设忽略了两个上限。首先,账户级别的并发执行限制是真实存在的,并且突发限制(初始 500–3,000,取决于区域,然后每分钟 +500)控制着扩展速率。一旦超过,同步调用者会收到 429 TooManyRequestsException,API Gateway 会将其呈现为 5xx 错误。其次,下游系统有其自身的限制:一个 max_connections=85 的 db.t3.micro 实例无法承受一千个并发的 Lambda 同时打开连接。PostgreSQL 为每个连接派生一个后端进程,消耗约 10 MB 内存;即使 CPU 有余,仅连接的建立和拆除就可能占满实例资源。
两种缓解措施是:(1) RDS Proxy,它将许多客户端连接汇集并复用到一小组持久的后端连接上;(2) 插入一个队列,从而将处理速率与请求到达速率解耦:
import psycopg2, os
conn = psycopg2.connect(
host=os.environ['PROXY_ENDPOINT'], # RDS Proxy, not the DB directly
dbname='orders', user='app', password=get_secret())
RDS Proxy 是一个最小化变更的解决方案——驱动程序和连接字符串几乎无需更改——这使得当需求是“对应用程序改动最小”并解决连接耗尽问题时,它成为正确的答案。DynamoDB 没有这个问题:它的 HTTPS API 是无状态的,这就是为什么 DynamoDB 与高扇出 (high-fanout) 的 Lambda 工作负载能天然地结合在一起。
Lambda IAM、环境变量与网络
每个函数在调用时都会承担一个执行角色。该角色的信任策略授予 lambda.amazonaws.com sts:AssumeRole 权限;其权限策略定义了函数可以执行的操作。运行时会自动将短期的 STS 凭证注入到 AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY 和 AWS_SESSION_TOKEN 中。绝不要将 IAM 用户访问密钥嵌入到环境变量或代码中——它们是静态的,在泄露的源代码或 CloudTrail 导出中可被发现,必须手动轮换,并且完全绕过了临时凭证模型。
FnRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal: { Service: lambda.amazonaws.com }
Action: sts:AssumeRole
Policies:
- PolicyName: ReadOrders
PolicyDocument:
Statement:
- Effect: Allow
Action: ["dynamodb:GetItem", "dynamodb:Query"]
Resource: !GetAtt OrdersTable.Arn
携带敏感配置的环境变量应使用客户管理的 KMS 密钥进行加密,并利用“传输中加密助手”功能,以便控制台在客户端进行加密。在 INIT 阶段解密一次,并将明文缓存到模块级变量中——否则每次调用都会产生一次 KMS API 调用开销。
默认情况下,函数在 AWS 管理的 VPC 中运行,拥有不受限制的出站互联网访问权限。仅当函数必须访问 VPC 内的资源(如私有子网中的 RDS、ElastiCache、通过 Direct Connect 连接的本地资源)时,才将其附加到您自己的 VPC。Lambda 随后会在您指定的子网中附加 Hyperplane ENI,并继承这些子网的路由规则。
一个典型的陷阱是将 Lambda 放置在私有子网中,而该子网除了通过 NAT 网关——或者更糟,通过自管理的 NAT 实例——之外,没有其他路由可用于 AWS 服务流量。NAT 实例的性能会受限于单个网卡,是单点故障 (SPOF),并且按 GB 计费。即使是 NAT 网关也要按 GB 收费,而且对于 AWS 服务流量来说并非必需。正确的模式是:
- 网关 VPC 端点用于 S3 和 DynamoDB (免费)
- 接口 (PrivateLink) 端点用于 KMS、Secrets Manager、SQS、SNS、STS 及类似服务
这样可以将流量保留在 AWS 骨干网络上,消除 NAT 的吞吐量上限,并防止意外的出口流量账单。请注意,Lambda ENI 永远不会获得公有 IP;将函数放置在“公有”子网中并不会使其获得互联网访问权限——您仍然需要在另一个带有适当路由的公有子网中配置 NAT 网关。
Amazon API Gateway:端点类型、授权与交付
API Gateway 提供三种 API 类型:
| 功能 | REST API | HTTP API | WebSocket |
|---|---|---|---|
| 延迟 | 更高 | 低约 60% | 有状态,双向 |
| 成本 | 更高 | 便宜约 70% | 按消息计费 |
| 使用计划 / API 密钥 | 支持 | 不支持 | 不支持 |
| 请求/响应转换 (VTL) | 支持 | 有限支持 | — |
| JWT 授权方 | 通过 Lambda | 原生支持 | 通过 Lambda |
| WAF | 支持 | 不支持 (需用 CloudFront 作为前端) | 支持 |
当您需要 API 密钥和使用计划、JSON Schema 请求验证、VTL 映射模板、带每方法作用域的 Cognito 授权方或 WAF 时,请选择 REST API;当成本和延迟更为重要,且用于轻量级的 JWT 认证代理模式时,请选择 HTTP API。
端点类型决定了 API 的前端部署位置:
- 边缘优化:由 API Gateway 管理的 CloudFront 分发提供前端;TLS 在最近的 POP (接入点) 终止。最适合全球分布的客户端。
- 区域性:在单个区域内暴露,不经过 CloudFront。最适合客户端位于同一区域、您希望自行部署 CloudFront 作为前端、或需要跨多个区域使用 Route 53 基于延迟的路由等场景。
- 私有:只能通过接口 VPC 端点访问。最适合那些绝不能穿越公共互联网的内部微服务。
为一个内部的、单区域的 API 选择边缘优化类型,会增加不必要的 CloudFront 跳转,并导致部署传播速度变慢。为一个全球访问的公共 API 选择区域性类型,会迫使每个请求都通过公共互联网访问到那一个区域。
自定义域名需要 ACM 证书,其所在区域取决于端点类型:边缘优化类型需要 us-east-1 的证书 (因为 CloudFront 是全球服务,在此处终止 TLS);区域性端点则需要 API 所在区域的证书。混淆这一点是常见的配置错误。基本路径映射允许一个域名复用给多个 API (例如 /orders → 订单 API, /users → 用户 API)。
授权有四种模型,绕过内置功能是一种经典的反模式:
| 机制 | 使用场景 |
|---|---|
| IAM 授权 | 调用方是能够签署 SigV4 的 AWS 主体 (如其他服务、SDK、跨账户调用) |
| Cognito 用户池授权方 | 用户通过 Cognito 用户池进行身份验证;API Gateway 负责验证 JWT |
| Lambda 授权方 | 处理非标准令牌、不支持 OIDC 的第三方身份提供商 (IdP)、或复杂的按请求授权逻辑 |
| API 密钥 + 使用计划 | 用于计量、限流、配额——绝不用作身份验证 |
自定义 Lambda 授权方会为每个请求(或在缓存 TTL 周期内)增加一次额外的函数调用,多出一个需要修补和监控的函数,并引入一个可能因签名验证 bug 而静默允许访问的代码路径。仅在内置功能确实无法满足需求时才使用它。
Lambda 代理集成是默认模式——整个请求被作为事件传递给函数,且函数必须返回特定格式的响应信封:
{
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": "{\"orderId\":\"abc123\"}",
"isBase64Encoded": false
}
对于 TPS 非常高的“即发即忘”式数据摄入场景,应使用 API Gateway 的直接 AWS 服务集成,将请求直接推送到 SQS 或 Kinesis,完全跳过 Lambda 这一环。这可以消除写入路径上的冷启动,并将摄入速率与处理能力解耦。
为了实现安全发布,可以在某个阶段上使用金丝雀部署:将一定百分比的流量引导至新部署,而大部分流量仍保留在稳定版本上,通过每个金丝雀部署的 CloudWatch 指标来决定是升级还是回滚。
aws apigateway update-stage --rest-api-id abc123 --stage-name prod \
--patch-operations \
op=replace,path=/canarySettings/percentTraffic,value=10 \
op=replace,path=/canarySettings/deploymentId,value=xyz789
对于一个简单的 webhook 场景,如果 API Gateway 的繁琐配置显得小题大做(例如单租户的 Slack 回调、GitHub 推送处理器),可以使用 Lambda 函数 URL,它为函数提供了一个专用的 HTTPS 端点。当调用方是 AWS 主体时,使用 AuthType: AWS_IAM 来保护它;如果 AuthType 为 NONE,则你必须在函数代码内部验证请求签名。一个 AuthType 为 NONE 且没有函数内验证的函数 URL,就是一个暴露在公共互联网上的匿名计算端点。
调用类型、重试和幂等性
事件源分为两大类,它们的行为截然不同。推送型事件源(API Gateway、ALB、S3、SNS、EventBridge、Cognito)直接调用 Lambda,并需要一个 AWS::Lambda::Permission 基于资源的策略,该策略授予 lambda:InvokeFunction 权限,并指定正确的 Principal(例如 events.amazonaws.com)和 SourceArn。如果没有这个策略,规则虽然能匹配到事件,但每次调用都会被静默拒绝——函数永远不会运行,失败记录只会出现在 CloudTrail 中。这与执行角色不同,执行角色管理的是函数可以做什么,而不是谁可以调用它。
轮询型事件源(SQS、Kinesis、DynamoDB Streams、MSK)由 Lambda 服务通过事件源映射(event source mapping)来读取——不需要基于资源的策略,但其执行角色必须授予读取权限。
调用类型进一步影响了重试行为:
- 同步(API Gateway、ALB、
RequestResponseSDK):错误会立即返回;由调用方负责重试。 - 异步(S3、SNS、EventBridge):Lambda 会在内部队列中缓冲事件,失败后会延迟重试两次,然后发送到死信队列 (DLQ) 或失败时目标 (on-failure destination)。
- SQS 事件源映射:SQS 会持续重新投递消息,直到达到
maxReceiveCount上限,然后将消息移至源队列的 DLQ。 - Kinesis / DynamoDB Streams:会重试单个分片批次直到成功或过期,在此期间会阻塞该分片——一个卡住的批次会暂停该分区所有下游处理。
因为重试机制内建于每一层,所以幂等性是强制性的,而非可选项。在缓慢写入过程中 SQS 可见性超时、或下游服务返回 5xx 错误后触发异步重试,都可能产生重复消息。典型的模式是使用确定性的键和 DynamoDB 的条件写入:
def handler(event, context):
msg_id = event['Records'][0]['messageId']
try:
ddb.put_item(
TableName='processed',
Item={'id': {'S': msg_id}, 'ttl': {'N': str(ttl)}},
ConditionExpression='attribute_not_exists(id)')
except ddb.exceptions.ConditionalCheckFailedException:
return # already processed
process(event)
AWS Lambda Powertools 的 @idempotent 装饰器正是实现了这种以 DynamoDB 为后端的模式。
使用 SQS 和 SNS 进行解耦
将高扇出(high-fanout)的推送型事件源直接连接到 Lambda 是很脆弱的。例如,S3 事件通知是按事件同步的,并且受 Lambda 并发上限的限制;如果在突发上传期间(例如营销活动在几秒钟内上传数千个文件),调用次数超过了可用并发,S3 的重试窗口很短,事件实际上可能被丢弃。补救措施是增加一个缓冲区:
| 模式 | 使用场景 |
|---|---|
| S3 → Lambda 直连 | 事件速率低且可预测;处理过程幂等 |
| S3 → SQS → Lambda | 突发性工作负载;需要重试/DLQ;下游有速率限制 |
| S3 → SNS → 多个 SQS | 扇出到多个独立的消费者 |
| S3 → EventBridge → 多个目标 | 跨账户路由;基于内容的筛选 |
SNS 是发布/订阅(pub/sub)模型:一次发布,多个订阅者(SQS、Lambda、HTTPS、电子邮件)。消息筛选是基于属性的。SQS 是一个持久化的点对点队列,消息最多可保留 14 天。最常用的组合是 SNS → SQS 扇出,为每个消费者提供各自的缓冲队列,以便独立扩展和重放。
对于严格排序的场景(例如,按客户顺序处理订单),应使用 SQS FIFO 队列,并将 MessageGroupId 设置为排序键。同一组内的消息按顺序交付;不同组的消息并行处理。标准 SQS 只提供尽力而为的排序。
典型的解耦摄取模式使用 API Gateway 的直接 SQS 集成来吸收突发流量,并由一个限速的处理器进行处理:
Resources:
OrdersQueue:
Type: AWS::SQS::Queue
Properties:
FifoQueue: true
ContentBasedDeduplication: true
RedrivePolicy:
deadLetterTargetArn: !GetAtt OrdersDLQ.Arn
maxReceiveCount: 5
ProcessorFunction:
Type: AWS::Lambda::Function
Properties:
ReservedConcurrentExecutions: 20 # cap the DB write rate
Mapping:
Type: AWS::Lambda::EventSourceMapping
Properties:
EventSourceArn: !GetAtt OrdersQueue.Arn
FunctionName: !Ref ProcessorFunction
BatchSize: 10
这里的预留并发是特意设置的——它限制了数据库写入的速度,从而让队列(而不是 RDS)来吸收突发流量。失败的消息在达到 maxReceiveCount 次数后会被路由到 DLQ 以供离线排查。
EventBridge:规则、输入转换和 API 目标
EventBridge 是一个支持 schema 的事件总线,具备丰富的 JSON 模式匹配、SaaS 合作伙伴源、schema 注册表以及归档/重放功能。规则基于事件模式进行匹配,并扇出到 30 多种目标类型(Lambda、Step Functions、SQS、Kinesis、ECS、Firehose),支持对消息正文进行基于内容的筛选——而不仅仅是像 SNS 那样基于属性:
{
"source": ["tenant.energy"],
"detail-type": ["UsageReported"],
"detail": { "kWh": [{ "numeric": [">", 100] }] }
}
一个规则最多可以有五个目标,每个目标可以接收原始事件或经过转换的子集。输入转换器 (Input transformers) 可实现松耦合:InputPathsMap 从事件中提取 JSON 路径,而 InputTemplate 则将其重塑为目标所期望的精确格式:
EventPattern:
source: ["com.acme.orders"]
detail-type: ["OrderPlaced"]
Targets:
- Arn: !GetAtt PaymentValidator.Arn
InputTransformer:
InputPathsMap:
orderId: "$.detail.orderId"
amount: "$.detail.total"
card: "$.detail.payment.cardToken"
InputTemplate: |
{"orderId": <orderId>, "amount": <amount>, "cardToken": <card>}
每个验证 Lambda 只接收它需要的数据;地址验证器永远不会看到信用卡令牌。这种方式远优于一个接收完整事件并在内部进行分支处理的单体 Lambda——单体模式会集中 IAM 权限(一个角色必须持有所有下游权限)、扩大 bug 的爆炸半径、耦合部署节奏、妨碍按职责分别调整内存/超时,并迫使整个函数根据流量最大分支的速率进行扩展。
API 目标 (API destinations) 则反转了方向:由 EventBridge 调用外部的 HTTPS 端点。通过与一个连接 (connection)(它将 Basic、API 密钥或 OAuth 凭证存储在 Secrets Manager 中)配对,这成为了一种无需 Lambda 的 serverless 方式,用于在 AWS Batch 作业成功等情况下通知第三方 SaaS。EventBridge 捕获状态变更事件,一条规则匹配 JobSucceeded,然后 API 目标会将从连接中注入的凭证通过 POST 请求发送给供应商。
计划规则(cron/rate 表达式)或更新的 EventBridge Scheduler 可以取代用于周期性任务(如每日报告、每小时缓存刷新)的心跳 EC2 实例。
当需要基于消息正文内容(而不仅是属性)进行筛选、当新消费者必须在不更改生产者的情况下加入、或者当路由涉及跨账户或 SaaS 源时,应选择 EventBridge 而不是 SNS。当扇出模型只是向一组固定的订阅者发送简单的、基于属性筛选的通知时,应选择 SNS。
Step Functions:工作流编排与分布式映射
当一个工作流包含多个步骤、分支、重试、人工审批或长时间等待时,将这些逻辑嵌入到链式调用的 Lambda 中会变得难以维护。Step Functions 使用 Amazon States Language 将状态机外化。
- 标准工作流:最长可运行一年,提供精确一次 (exactly-once) 的语义,保留完整的执行历史,通过
.waitForTaskToken实现人工/外部审批。适用于订单履行、ETL、审批流等场景。 - 快速工作流:最长可运行五分钟,提供至少一次 (at-least-once) 的语义,支持高吞吐量,单次执行成本低。适用于 API Gateway 后面的短时同步编排、物联网和流数据转换。
每个任务都应明确声明 Retry 和 Catch:
"ValidatePayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {"FunctionName": "PaymentValidator", "Payload.$": "$"},
"Retry": [{
"ErrorEquals": ["Lambda.ServiceException", "Lambda.TooManyRequestsException"],
"IntervalSeconds": 2, "MaxAttempts": 4, "BackoffRate": 2.0
}],
"Catch": [{"ErrorEquals": ["PaymentDeclined"], "Next": "RefundStep"}],
"Next": "ShipOrder"
}
Parallel 和 Map 状态可以并发运行多个分支并聚合结果——这非常适合具有独立验证器(如地址、库存、支付验证)的订单系统。步骤之间的状态通过执行的 JSON 文档流动,无需为了工作流协调而专门使用共享数据库。
.waitForTaskToken 模式会暂停执行,直到外部参与者使用该令牌调用 SendTaskSuccess 为止——当工作流需要跨越 Lambdas、EC2、容器、本地系统,并要求以最少的运维开销实现人工审批时,这便是经典的解决方案:
"ManagerApproval": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish.waitForTaskToken",
"Parameters": {
"TopicArn": "arn:aws:sns:us-east-1:111:approvals",
"Message": { "TaskToken.$": "$$.Task.Token", "OrderId.$": "$.orderId" }
},
"Next": "Fulfill"
}
分布式映射 (Distributed Map) 扩展了标准的 Map 状态,能够处理多达 10,000 个并行子执行,并且可以直接迭代 S3 存储桶中的对象或 CSV/JSONL 文件中的行,同时提供自动批处理、检查点和容错功能。对于数千个半结构化的 S3 对象,这是运维效率最高的选项——只需将其指向一个前缀,定义好针对每个项目的任务,Step Functions 就会处理扇出 (fan-out)、MaxConcurrency、重试和结果聚合:
{
"Type": "Map",
"ItemReader": {
"Resource": "arn:aws:states:::s3:listObjectsV2",
"Parameters": { "Bucket": "raw-events", "Prefix": "2024/" }
},
"MaxConcurrency": 1000,
"ItemProcessor": {
"ProcessorConfig": { "Mode": "DISTRIBUTED", "ExecutionType": "STANDARD" },
"StartAt": "ProcessObject",
"States": { "ProcessObject": { "Type": "Task", "Resource": "arn:aws:lambda:...:function:ProcessOne", "End": true } }
}
}
如果在 SQS 或 EventBridge 上重建相同的功能,则需要自定义逻辑来跟踪任务完成、重试和结果组装。
Step Functions vs. EventBridge: 当你需要控制序列和结果——包括状态、分支、重试、审批时,Step Functions 是正确的选择。当生产者不关心谁是消费者,而消费者可以独立地附加到事件流时,EventBridge 是正确的选择。
S3 事件通知
要近实时地处理上传的对象,可以为 s3:ObjectCreated:* 事件(或更具体的变体,如 Put、Post、CompleteMultipartUpload)配置 S3 事件通知,并将 Lambda 作为目标——但需注意前述的突发流量限制。防护措施:
- 设置预留并发以保护下游数据库。
- 为毒丸消息启用死信队列 (DLQ) 或失败时目标。
- 当多个消费者需要相同的事件时,通过 EventBridge 路由 S3 事件。直接的 S3 通知配置在数量和表达能力上都有限制;而 EventBridge 的扇出 (fan-out) 模式,配合针对每个目标的输入转换器,具有更好的扩展性。
流处理:Kinesis Data Streams vs. Firehose
Kinesis Data Streams (KDS) 是一个分片的、有序的、可重放的日志,数据保留期为 24 小时至 365 天。顺序在每个分片内通过分区键来保证——这对于按设备或按租户进行聚合至关重要。多个消费者可以独立读取(使用增强型扇出可为每个消费者提供隔离的吞吐量)。当需要有序重放、多个独立消费者或每个分片需要高吞吐量时,应选择 KDS。
Kinesis Data Firehose 是一个完全托管的交付流,可将数据传输到 S3、Redshift、OpenSearch 或 Splunk。它内置了缓冲功能(60 秒或 1–128 MB)、可选的 Lambda 转换、压缩(GZIP、Snappy)以及 Parquet/ORC 格式转换。无需管理分片。当不需要自定义消费者和重放功能,而只是需要落地近实时数据时,Firehose 是低运维开销的选择。
经典的近实时分析管道是:生产者 → KDS → Firehose → S3 (Parquet) → Athena/QuickSight,并可在 Firehose 中通过 Lambda 进行可选的数据扩充。
AWS Transfer Family:托管的 SFTP 服务
当合作伙伴要求通过 SFTP、FTPS 或 FTP 向 S3 或 EFS 传入或传出文件时,AWS Transfer Family 提供了一个托管的、跨可用区的终端节点,该终端节点支持客户端已在使用的协议。身份验证支持服务管理的用户、SSH 密钥,或通过 API Gateway/Lambda 实现的自定义身份提供商 (IdP)。文件直接存入 S3,并应用服务器端加密 (SSE) 和生命周期策略;IAM 范围缩减策略可以将每个用户限制在特定的 S3 前缀下。
如果在 EC2 上自行构建此功能,则需要进行 OpenSSH 加固、打补丁、实现跨可用区的高可用性 (HA)、密钥轮换和日志传送——所有这些工作都由 Transfer Family 承担了。只要需求是“合作伙伴通过 SFTP 向我们发送文件”,并且你希望以最少的运维开销将文件存入 S3,就应该选择它。
构建一个经典的无服务器模式
一个用于处理按租户的每小时指标的低开销摄取设计:传感器通过 API Gateway (HTTP API, regional) 发送 POST 请求 → Lambda 进行验证并将事件发布到 EventBridge → EventBridge 规则将事件路由到一个写入 DynamoDB 的 Lambda(租户 ID 作为分区键,小时桶作为排序键),同时并行地路由到 Firehose → S3 (Parquet) 以进行分析。新的消费者可以作为额外的 EventBridge 规则附加,而无需改动生产者——这是仅使用 SNS 无法如此清晰地满足的可扩展性要求。在需要持续高吞吐量和保证顺序的场景下(如账单对账、金融事件流),应用 Kinesis Data Streams 替换 EventBridge,并使用增强型扇出 (enhanced fan-out) 来支持独立消费者。
陷阱目录:常见错误模式失败的原因
私有子网中的 Lambda 通过 NAT 实例/网关访问 AWS 服务。 NAT 实例的吞吐量受限于单个 EC2 网卡且存在单点故障 (SPOF);NAT 网关按 GB 计费。当访问目标是 AWS 服务时,这两种方式都不是必需的。访问 S3/DynamoDB 应使用网关终端节点,访问其他服务则使用接口终端节点。
在延迟敏感的 API 中忽略冷启动问题。 按需模式仅在请求到达时才预置容器,因此突发流量会导致服务不满足 SLA。应根据 p95 突发流量来设置预置并发;对于没有严格 SLA 要求的 Java 工作负载,可使用 SnapStart。
单体 Lambda 接收完整事件并在内部进行分支处理。 这违反了最小权限原则(一个角色持有所有下游权限)、耦合了部署节奏、妨碍了按职责分别调优,并导致整个函数根据最繁忙分支的速率进行伸缩。应按职责拆分函数;使用 EventBridge 或 Step Functions 进行粘合。
大量 Lambda 函数直连数据库。 总连接数等于并发调用数,因为容器之间不共享连接池。可通过 RDS Proxy(连接池)、预留并发(速率限制)或 SQS(缓冲)来解决。
使用同步 Lambda 处理突发性数据采集。 超出并发限制时,API Gateway 会返回 429,客户端会收到 5xx 错误;突发期间,S3 的直接通知会静默丢弃事件。应在中间插入 SQS,或使用 API Gateway → SQS 的直接集成。
使用 AuthType: NONE 且函数内无签名验证的公开 Lambda 函数 URL。 这相当于在公共互联网上提供匿名计算。对于 AWS 调用方应使用 AWS_IAM 认证,对于第三方 webhook 则应验证签名。
在有内置授权方可用时仍使用自定义 Lambda 授权方。 这会增加延迟、引入另一个需要打补丁的函数,并增加了一条可能因签名检查漏洞而静默允许访问的代码路径。对于 AWS 主体,首选 IAM 授权;对于用户池,首选 Cognito 授权方。
为自定义域名使用了错误区域的 ACM 证书。 边缘优化的终端节点要求证书在 us-east-1 区域;区域终端节点要求证书在 API 所在的区域。
对于推送型事件源(EventBridge、S3、SNS)缺少 AWS::Lambda::Permission。 事件能匹配规则,但调用请求会被静默拒绝。这与执行角色不同——它规定的是谁可以调用该函数,而不是该函数可以做什么。
缺少幂等性处理。 每一层都可能重试——异步调用、SQS 可见性超时后的重新投递、Kinesis 批处理重试。如果没有确定性的去重键和条件写入,重复处理将不可避免。
练习这些题目 → · 在 ExamRoll.io 上限时练习 →
Pass the whole exam — not just this question
You found this answer. Get every verified question and explanation in one place, and save hours of prep. Free to start.
通过考试 →