Amazon SAA-C03: 無伺服器與事件驅動架構 / API 整合 — 學習指南
屬於 AWS SAA-C03 — 完整學習指南. 使用經過驗證的解答練習: Amazon 考試中心, 或參加限時模擬考試: ExamRoll.io.
AWS Lambda:執行模型、執行期生命週期與冷啟動
Lambda 在隔離的 Firecracker 微型虛擬機 (micro-VM) 中執行程式碼,並遵循一個兩階段的生命週期。INIT 階段會佈建容器、啟動執行期、下載並解壓縮部署套件,並執行任何模組層級的初始化——例如 SDK 客戶端建構、載入經 KMS 解密的密鑰、設定資料庫連線池、JVM 類別載入與 JIT 預熱。INVOKE 階段則執行處理常式 (handler)。一次冷啟動會產生完整的 INIT 成本;而暖調用 (warm invocation) 則會重複使用該環境,因此任何在處理常式之外快取的資源(如 HTTP keep-alive 連線池、已解密的密鑰、資料庫客戶端)在該容器上的後續調用中都會被保留。這就是為什麼標準模式是在模組範疇 (module scope) 內建構昂貴的物件並重複使用它們:
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 程式碼仍然是主要影響因素。
有三種工具能以不同方式處理冷啟動問題:
| 功能 | 行為 | 成本 | 最適合情境 |
|---|---|---|---|
| 隨需並行 (On-demand concurrency) | 預設;以約 500–3,000 的初始突發量擴展,之後每分鐘增加 500 | 按調用次數 + 持續時間計費 | 突發性、可容忍延遲的流量 |
| 預置並行 (Provisioned concurrency) | 預先初始化 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 並行控制與下游保護
有三個重要的並行調節器,各自有不同的功用:
- 保留並行 (Reserved concurrency) 會限制並保留一個函式可以使用的並行執行數量。它會從帳戶的共用池中劃出一部分容量,並保護下游系統(例如一個小型的 RDS 實例、一個有速率限制的合作夥伴 API)不被過量請求淹沒。
- 預置並行 (Provisioned concurrency) 預先初始化環境——這是延遲的調節器,而不是擴展的調節器。
- 未保留帳戶並行 (Unreserved account concurrency) 是共享池,每個區域預設為 1,000。
認為 Lambda 可以「無限擴展」的假設,忽略了兩個上限。首先,帳戶層級的並行執行上限是真實存在的,而且突發限制(初始 500–3,000,依區域而定,之後每分鐘 +500)決定了擴展速率。一旦超過上限,同步呼叫者會收到 429 TooManyRequestsException,API Gateway 會將其呈現為 5xx 錯誤。其次,下游系統有自己的限制:一個 max_connections=85 的 db.t3.micro 資料庫實例,無法承受一千個並行的 Lambda 各自開啟一個連線。PostgreSQL 會為每個連線 fork 一個後端程序,消耗約 10 MB 的 RAM;即使 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、環境變數與網路
每個函數在被調用時都會擔任一個 execution role (執行角色)。該角色的信任政策 (trust policy) 會將 sts:AssumeRole 權限授予 lambda.amazonaws.com;其許可政策 (permission policy) 則定義了函數可以執行的操作。Runtime 會自動將短期的 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 金鑰進行加密,並利用「傳輸中加密輔助程式」(helpers for encryption in transit),讓主控台在客戶端進行加密。在 INIT 階段解密一次,並將明文快取在模組層級的變數中——否則每次調用都會產生一次 KMS API 呼叫的成本。
預設情況下,函數在一個 AWS 管理的 VPC 中運行,並擁有不受限制的對外網際網路存取權。只有當函數需要存取 VPC 內的資源(例如私有子網路中的 RDS、ElastiCache、或透過 Direct Connect 連接的本地端資源)時,才將其附加到您自己的 VPC。Lambda 隨後會將 Hyperplane ENI 附加到您指定的子網路中,並繼承其路由規則。
一個常見的誤區是將 Lambda 放在一個私有子網路中,而該子網路通往 AWS 服務的流量只能透過 NAT 閘道——或者更糟的是,透過自行管理的 NAT 執行個體。NAT 執行個體的吞吐量受限於單一網卡,是一個單點故障 (SPOF),並且按 GB 計費。即使是 NAT 閘道,也是按 GB 收費,且對於 AWS 服務流量而言並非必要。正確的模式是:
- 使用 Gateway VPC endpoints (閘道 VPC 端點) 連接 S3 和 DynamoDB (免費)
- 使用 Interface (PrivateLink) endpoints (介面端點) 連接 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 映射樣板、帶有各方法範圍 (per-method scopes) 的 Cognito 授權方或 WAF 時,請選擇 REST API;當成本和延遲更為重要,且用於輕量級 JWT 驗證的代理模式時,請選擇 HTTP API。
端點類型決定了 API 的前端位置:
- Edge-optimized (邊緣優化):由 Gateway 管理的 CloudFront 分發提供前端服務;TLS 在最近的 POP (接入點) 終止。最適合全球分佈的客戶端。
- Regional (區域性):在單一 Region 中暴露,不經過 CloudFront。最適合客戶端也在同一 Region、您想在前面放置自己的 CloudFront、或使用 Route 53 延遲路由跨多個 Region 的情況。
- Private (私有):只能透過介面 VPC 端點存取。最適合不應經過公用網際網路的內部微服務。
為一個內部的、單一 Region 的 API 選擇邊緣優化類型,會增加不必要的 CloudFront 躍點,並減慢部署的傳播速度。為一個全球使用的公有 API 選擇區域性類型,則會迫使每個請求都跨越公用網際網路到達單一 Region。
Custom domains (自訂網域) 需要 ACM 憑證,其位置取決於端點類型:邊緣優化類型需使用 us-east-1 的憑證 (因為 CloudFront 是全球性的,並在此處終止 TLS);區域性端點則使用 API 所在 Region 的憑證。混淆這一點是常見的配置錯誤。基礎路徑對應 (Base path mappings) 讓一個網域可以複用給多個 API (例如 /orders → orders API,/users → users API)。
授權有四種模型,繞過內建機制是一個典型的反模式:
| 機制 | 使用時機 |
|---|---|
| IAM authorization (IAM 授權) | 呼叫者是能夠簽署 SigV4 的 AWS principals (其他服務、SDK、跨帳戶) |
| Cognito user pool authorizer (Cognito 使用者集區授權方) | 使用者透過 Cognito 使用者集區進行身份驗證;Gateway 驗證其 JWT |
| Lambda authorizer (Lambda 授權方) | 非標準 token、不支援 OIDC 的第三方 IdP、複雜的逐次請求邏輯 |
| API keys + usage plans (API 金鑰 + 用量計畫) | 用於計量、調節、配額——絕不用作身份驗證 |
自訂的 Lambda 授權方會為每個請求 (或在快取 TTL 內) 增加一次額外的調用、一個需要修補和監控的額外函數,以及一條可能因簽章驗證錯誤而悄悄允許存取的程式碼路徑。只有當內建機制確實無法滿足需求時才使用它。
Lambda proxy integration (Lambda 代理整合) 是預設模式——整個請求作為一個事件傳遞,函數必須返回指定的回應封包格式:
{
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": "{\"orderId\":\"abc123\"}",
"isBase64Encoded": false
}
對於需要極高 TPS 的「發後不理」(fire-and-forget) 式資料擷取,可使用 Gateway 的直接 AWS 服務整合,直接將資料推送到 SQS 或 Kinesis,完全跳過 Lambda 這一環。這消除了寫入路徑上的冷啟動問題,並將資料擷取速率與處理能力解耦。
為了安全地進行版本更新,請在一個 stage (階段) 上使用 canary deployments (Canary 部署):將一定百分比的流量導向新的部署,而大部分流量仍留在穩定版本上,根據每個 Canary 的 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 function URLs (Lambda 函數 URL),它直接在函數上提供一個專用的 HTTPS 端點。當呼叫者是 AWS principals 時,使用 AuthType: AWS_IAM 來保護它;如果設為 NONE,您必須在函數內驗證請求簽章。一個 AuthType: NONE 且沒有函數內驗證的函數 URL,就等於是一個在公用網際網路上的匿名運算端點。
呼叫類型、重試與冪等性
事件來源可分為兩大類,其行為截然不同。推送型來源 (Push-based sources)(如 API Gateway、ALB、S3、SNS、EventBridge、Cognito)會直接呼叫 Lambda,並需要一個 AWS::Lambda::Permission 資源型政策,授予 lambda:InvokeFunction 權限,且 Principal (例如 events.amazonaws.com) 和 SourceArn 必須正確。若缺少此政策,即使規則匹配到事件,每次呼叫也都會被靜默拒絕——函式永遠不會執行,失敗紀錄只會出現在 CloudTrail 中。這與執行角色 (execution role) 不同,後者是管理函式可以做什麼,而不是誰可以呼叫它。
輪詢型來源 (Poll-based sources)(如 SQS、Kinesis、DynamoDB Streams、MSK)則是由 Lambda 服務透過事件來源映射 (event source mapping) 來讀取——不需要資源型政策,但執行角色必須授予讀取權限。
呼叫類型進一步影響重試行為的分支:
- 同步 (Synchronous) (API Gateway、ALB、
RequestResponseSDK):錯誤會立即回傳;由呼叫端負責重試。 - 非同步 (Asynchronous) (S3、SNS、EventBridge):Lambda 會在內部佇列中緩衝,並在失敗時帶有延遲地重試兩次,然後傳送到 DLQ 或失敗時目的地 (on-failure destination)。
- SQS 事件來源映射:SQS 會持續重新交付,直到
maxReceiveCount觸發訊息移至來源佇列的 DLQ。 - Kinesis / DynamoDB Streams:會重試單一批次的 shard 資料,直到成功或過期為止,在此期間會阻塞該 shard——一個卡住的批次會拖累該分割區 (partition) 所有下游的處理。
因為重試機制內建於每一層,冪等性 (idempotency) 是強制性的,而非選擇性的。一個 SQS 可見性逾時 (visibility timeout) 在緩慢寫入期間到期,或是一個下游服務回傳 5xx 錯誤後的非同步重試,都會產生重複的訊息。標準的模式是使用確定性的鍵 (deterministic key) 和 DynamoDB 的條件式寫入 (conditional write):
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 裝飾器 (decorator) 正是實作了這種以 DynamoDB 為後端的模式。
使用 SQS 和 SNS 進行解耦
將高扇出 (high-fanout) 的推送型來源直接連接到 Lambda 是很脆弱的。舉例來說,S3 事件通知是每個事件同步觸發,且受限於 Lambda 的並行數量上限 (concurrency ceiling);如果在突發性上傳(例如行銷活動在幾秒內投放數千個文件)期間,呼叫次數超過了可用的並行數量,S3 的重試窗口很短,事件實際上可能會被丟棄。解決方法是加入一個緩衝區:
| 模式 | 使用時機 |
|---|---|
| S3 → Lambda direct | 事件速率低且可預測;冪等處理 |
| S3 → SQS → Lambda | 突發性工作負載;需要重試/DLQ;下游有速率限制 |
| S3 → SNS → multiple SQS | 扇出 (Fan-out) 到多個獨立的消費者 |
| S3 → EventBridge → many targets | 跨帳戶路由;基於內容的篩選 |
SNS 是發布/訂閱 (pub/sub) 模型:一次發布,多個訂閱者(SQS、Lambda、HTTPS、電子郵件)。訊息篩選是基於屬性 (attribute) 的。SQS 是一個持久的點對點佇列,訊息最多可保留 14 天。最主力好用的組合是 SNS → SQS 扇出,讓每個消費者都有自己的緩衝佇列,以便獨立擴展和重播。
若要嚴格排序(例如,每個客戶的訂單按順序處理),請使用 SQS FIFO 佇列,並將 MessageGroupId 設為排序鍵。同一群組內的訊息會按順序交付;不同群組則平行處理。標準 SQS 只提供盡力而為 (best-effort) 的排序。
標準的解耦擷取模式是使用 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
保留並行數量 (reserved concurrency) 是刻意設定的——它限制了資料庫寫入的速度,因此由佇列(而不是 RDS)來吸收突發流量。失敗的訊息在達到 maxReceiveCount 後會被路由到 DLQ 以供離線檢查。
EventBridge:規則、輸入轉換與 API 目的地
EventBridge 是一個感知結構描述 (schema-aware) 的事件匯流排 (event bus),具備豐富的 JSON 模式匹配、SaaS 合作夥伴來源、結構描述註冊表 (schema registry) 以及封存/重播 (archive/replay) 功能。規則 (Rules) 根據事件模式進行匹配,並扇出到 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 只會收到它需要的資料;地址驗證器永遠不會看到信用卡權杖 (card token)。這顯然優於一個接收完整事件並在內部進行分支處理的單體式 (monolithic) Lambda——單體式架構會集中 IAM 權限(一個角色必須持有所有下游權限)、擴大錯誤的爆炸半徑、耦合部署節奏、妨礙按職責調整記憶體/逾時,並迫使整個函式必須根據最頻繁分支的速率來擴展。
API 目的地 (API destinations) 反轉了方向:由 EventBridge 呼叫外部的 HTTPS 端點。搭配一個將 Basic、API 金鑰或 OAuth 憑證儲存在 Secrets Manager 中的連線 (connection),這是在 AWS Batch 作業成功時通知第三方 SaaS 的無伺服器 (serverless) 方法——完全不需要 Lambda。EventBridge 捕獲狀態變更事件,一條規則匹配 JobSucceeded,然後 API 目的地目標會使用從連線中注入的憑證向供應商發出 POST 請求。
排程規則 (Scheduled rules)(cron/rate 表達式)或更新的 EventBridge Scheduler 取代了用於執行週期性任務(如每日報表、每小時快取刷新)的心跳 EC2 執行個體。
當篩選條件是基於訊息內文(不只是屬性)、當新的消費者必須在不更動生產者的情況下加入、或當路由需要跨帳戶或 SaaS 來源時,請選擇 EventBridge 而非 SNS。當扇出是針對一組穩定的訂閱者,且只需簡單的屬性篩選通知時,請選擇 SNS。
Step Functions:協同運作與分散式映射
當一個工作流程包含超過幾個步驟、分支、重試、人工批准或長時間等待時,將這些邏輯嵌入到鏈式 Lambda 中會變得難以維護。Step Functions 透過 Amazon States Language 將狀態機外部化。
- 標準工作流程 (Standard workflows):最長可達一年、exactly-once 語意、完整的執行歷史紀錄、用於人工/外部關卡的
.waitForTaskToken。適用於訂單履行、ETL、批准流程。 - 快速工作流程 (Express workflows):最長五分鐘、at-least-once 語意、高流量、每次執行成本低廉。適用於 API Gateway 後端的簡短同步協同運作;IoT 和串流資料轉換。
每個任務都應該明確宣告 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 模式會暫停執行,直到外部參與者使用該 token 呼叫 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 為目標——但需注意上述的突發流量限制。防護措施:
- 設定預留並行 (reserved concurrency) 以保護下游的資料庫。
- 啟用 DLQ 或失敗時的目的地 來處理無效訊息 (poison messages)。
- 當多個消費者需要相同的事件時,透過 EventBridge 路由 S3 事件。直接的 S3 通知設定在數量和表達能力上都有限;EventBridge 的扇出功能搭配針對每個目標的輸入轉換器,擴展性要好得多。
串流:Kinesis Data Streams vs. Firehose
Kinesis Data Streams (KDS) 是一個分片式、有序、可重播的日誌,資料保留期為 24 小時至 365 天。順序是在每個分片 (shard) 內根據分割區索引鍵 (partition key) 來保證的——這對於按裝置或按租戶進行匯總至關重要。多個消費者可以獨立讀取(增強型扇出可為每個消費者提供隔離的吞吐量)。當你需要有序重播、多個獨立消費者或每個分片的高吞吐量時,請選擇 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 範圍縮減政策 (scope-down policies) 將每個使用者限制在特定的前綴內。
若要在 EC2 上自行建構此功能,需要進行 OpenSSH 強化、修補、跨可用區的高可用性、金鑰輪換和日誌傳送——所有這些都由 Transfer Family 承擔了。每當需求是「合作夥伴透過 SFTP 將檔案傳送給我們」,並且你希望以最少的維運開銷將檔案存入 S3 時,就選擇它。
組合一個典型的無伺服器模式
一個用於處理每個租戶每小時指標的低維運開銷的資料擷取設計:感測器 POST 到 API Gateway (HTTP API, regional) → Lambda 進行驗證並發佈到 EventBridge → 規則將事件路由到一個寫入 DynamoDB 的 Lambda(租戶 ID 作為分割區索引鍵,小時區間作為排序鍵),並且,並行地,路由到 Firehose → S3 (Parquet) 以進行分析。新的消費者只需作為額外的 EventBridge 規則附加,而無需觸及生產者——這是僅使用 SNS 無法如此乾淨地滿足的可擴展性要求。在需要持續高吞吐量和順序性的場景(如帳單對帳、金融事件串流),則用 Kinesis Data Streams 取代 EventBridge,並使用增強型扇出 (enhanced fan-out) 來支援獨立的消費者。
陷阱目錄:常見錯誤模式為何會失敗
使用 NAT 執行個體/閘道來處理來自私有子網路 Lambda 前往 AWS 服務的流量。 NAT 執行個體會將吞吐量綁定在單一 EC2 的網路卡上,且是個單點故障 (SPOF);NAT 閘道則以 GB 計費。對於目的地是 AWS 服務的流量來說,這兩者都不是必要的。對 S3/DynamoDB 請使用 gateway endpoint,對其他所有服務則使用 interface endpoint。
在對延遲敏感的 API 上忽略冷啟動問題。 On-demand 模式只在請求抵達時才配置容器,因此突發流量會錯失 SLA。應將 provisioned concurrency 的大小設定為 p95 的突發流量;對於沒有嚴格 SLA 要求的 Java 工作負載,則使用 SnapStart。
單體式 Lambda 接收完整事件並在內部進行分支處理。 這違反了最小權限原則(單一角色持有所有下游服務的權限)、耦合了部署的節奏、妨礙了依據不同職責進行個別調校,且會根據最吵雜的分支流量來擴展整個函數。應按職責拆分,並使用 EventBridge 或 Step Functions 來串連。
由大量 Lambda 直接連線到資料庫。 總連線數等於並行調用數,因為容器之間不共享連線池。可使用 RDS Proxy(連線池化)、reserved concurrency(速率上限)或 SQS(緩衝)來解決。
使用同步 Lambda 處理突發的資料擷取。 超出並行數量上限時,會對 API Gateway 回傳 429,並對客戶端回傳 5xx 錯誤;在突發流量期間,S3 的直接通知會無聲地丟棄事件。應插入 SQS,或使用 API Gateway → SQS 的直接整合。
將 Lambda function URL 設為公開 (AuthType: NONE) 且函數內未做簽章驗證。 這相當於在公開網際網路上提供匿名運算。對於來自 AWS 內部的呼叫者,應使用 AWS_IAM;對於第三方的 webhook,則應驗證其簽章。
在有內建機制可用的情況下,仍使用自訂的 Lambda authorizer。 這增加了延遲、多了一個需要修補的函數,並增加了一條可能因簽章檢查的 bug 而無聲地允許存取的程式碼路徑。對於 AWS principal,應優先使用 IAM authorization;對於 user pool,則應使用 Cognito authorizer。
用於自訂網域的 ACM 憑證 Region 不正確。 Edge-optimized 端點需要位於 us-east-1 的憑證;regional 端點則需要位於 API 所在 Region 的憑證。
針對 push 模式的事件來源 (EventBridge、S3、SNS) 缺少 AWS::Lambda::Permission。 事件符合規則,但調用會被無聲地拒絕。這與執行角色 (execution role) 不同——這個權限掌管的是「誰可以呼叫」此函數,而不是此函數「可以做什麼」。
缺少冪等性 (idempotency) 處理。 每一層都會重試——非同步調用、SQS 在 visibility-timeout 到期後的重新交付、Kinesis 的批次重試。如果沒有一個確定性的去重鍵 (dedup key) 和條件式寫入,重複的資料處理將無法避免。
練習這些題目 → · 在 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.
通過考試 →