Amazon SAA-C03: Architetture Serverless e Guidate dagli Eventi / Integrazione API — Guida allo studio
Fa parte della AWS SAA-C03 — Guida di studio completa. Esercitati con risposte verificate nel centro esami Amazon, oppure fai test cronometrati su ExamRoll.io.
AWS Lambda: Modello di Esecuzione, Ciclo di Vita del Runtime e Cold Start
Lambda esegue il codice in micro-VM Firecracker isolate che seguono un ciclo di vita a due fasi. La fase INIT esegue il provisioning del container, il bootstrap del runtime, scarica e decomprime il pacchetto di deployment ed esegue qualsiasi inizializzazione a livello di modulo: costruzione di client SDK, caricamento di segreti decifrati con KMS, configurazione del pool di connessioni al database, caricamento delle classi JVM e warm-up del JIT. La fase INVOKE esegue l’handler. Un cold start comporta l’intero costo della fase INIT; un’invocazione warm riutilizza l’ambiente, quindi tutto ciò che viene memorizzato nella cache al di fuori dell’handler (pool di connessioni HTTP keep-alive, segreti decifrati, client DB) persiste tra le invocazioni su quel container. Questo è il motivo per cui il pattern canonico consiste nel costruire oggetti costosi a livello di modulo e riutilizzarli:
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):
...
L’entità del cold start varia a seconda del runtime. Node.js e Python richiedono da decine a qualche centinaio di millisecondi; la JVM e .NET possono superare il secondo. Per le funzioni collegate a una VPC, le ENI Hyperplane hanno in gran parte ammortizzato la penalità storica legata all’associazione delle ENI, ma le dimensioni del pacchetto e un codice INIT pesante rimangono i fattori dominanti.
Tre strumenti affrontano i cold start in modi diversi:
| Caratteristica | Comportamento | Costo | Ideale per |
|---|---|---|---|
| Concurrency on-demand | Predefinita; scala con un burst iniziale di ~500–3.000, poi +500/min | Per invocazione + durata | Carichi bursty, tolleranti alla latenza |
| Concurrency provisioned | Pre-inizializza N ambienti, fase INIT completata prima del traffico | Pagamento per le unità provisioned 24/7 + invocazioni | SLA di latenza p99 stringenti |
| SnapStart (Java, Python, .NET) | Snapshot Firecracker dopo la fase INIT; ripristinato al cold start | Nessun costo aggiuntivo su Java; un piccolo costo di caching per gli altri | Funzioni Java dove il provisioning completo è uno spreco |
SnapStart è la soluzione economicamente più vantaggiosa per i carichi di lavoro Java senza contratti di latenza rigidi: i cold start si riducono di circa un ordine di grandezza senza costi aggiuntivi per invocazione. La concurrency provisioned è la risposta corretta quando un’API sincrona deve mantenere un p99 al di sotto, diciamo, di 100 ms durante i picchi; dimensionarla sul p95 del burst chiude il gap della tail-latency. Sottodimensionarla è un errore classico: la modalità on-demand avvia nuovi container solo quando arriva una richiesta, quindi un picco di traffico produce attese di diversi secondi per gli utenti reali.
# SAM: SnapStart on a Java 17 function
MyJavaFn:
Type: AWS::Serverless::Function
Properties:
Runtime: java17
SnapStart: { ApplyOn: PublishedVersions }
AutoPublishAlias: live
La stessa concurrency provisioned può essere scalata tramite Application Auto Scaling: un’azione pianificata che aumenta le unità da 10 a 200 alle 07:45 e le riporta indietro alle 10:00 evita di pagare per la capacità warm durante la notte, eliminando al contempo i cold start dovuti al picco mattutino.
Il dimensionamento della memoria è contemporaneamente un selettore della CPU: la vCPU scala linearmente con la memoria fino a ~1.769 MB per vCPU. Una funzione fissata a 128 MB può costare complessivamente di più di una a 1.024 MB, perché un tempo di esecuzione raddoppiato spesso supera un prezzo per ms raddoppiato. Non tirare a indovinare: usa AWS Lambda Power Tuning per testare diverse configurazioni con payload rappresentativi.
Controlli di Concorrenza di Lambda e Protezione dei Sistemi a Valle
Tre selettori di concorrenza sono importanti e ognuno svolge un compito diverso:
- Reserved concurrency limita e riserva il numero di esecuzioni concorrenti che una funzione può consumare. Ritaglia una parte della capacità dal pool dell’account e protegge i sistemi a valle (una piccola istanza RDS, un’API di partner con rate limit) dal sovraccarico.
- Provisioned concurrency pre-inizializza gli ambienti: è la leva per la latenza, non per la scalabilità.
- Unreserved account concurrency è il pool condiviso, con un default di 1.000 per Regione.
Presumere che Lambda “scali all’infinito” ignora due limiti massimi. Primo, il limite di esecuzioni concorrenti a livello di account è reale, e i limiti di burst (iniziali 500–3.000 a seconda della Regione, poi +500/min) governano la velocità di aumento (ramp rate). Una volta superato, i chiamanti sincroni ricevono un’eccezione 429 TooManyRequestsException, che API Gateway espone come un errore 5xx. Secondo, i sistemi a valle hanno i propri limiti: un’istanza db.t3.micro con max_connections=85 non può sopravvivere a mille Lambda concorrenti che aprono ciascuna una connessione. PostgreSQL crea un processo di backend per ogni connessione, consumando ~10 MB di RAM; anche con CPU a disposizione, la sola creazione e chiusura delle connessioni può saturare l’istanza.
Le due mitigazioni sono (1) RDS Proxy, che mette in pool e multiplexa molte connessioni lato client su un piccolo insieme di connessioni di backend persistenti, e (2) inserire una coda in modo che la velocità di elaborazione sia disaccoppiata dalla velocità di arrivo delle richieste:
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 è una soluzione a modifica minima — il driver e la stringa di connessione cambiano a malapena — il che la rende la risposta giusta ogni volta che il requisito è “la minor modifica possibile all’applicazione” unito all’esaurimento delle connessioni. DynamoDB non ha questo problema: la sua API HTTPS è stateless, motivo per cui DynamoDB si abbina naturalmente a carichi di lavoro Lambda con un elevato fan-out.
IAM, Variabili d’Ambiente e Networking di Lambda
Ogni funzione assume un ruolo di esecuzione (execution role) al momento dell’invocazione. La policy di attendibilità (trust policy) del ruolo concede sts:AssumeRole a lambda.amazonaws.com; la sua policy di autorizzazione (permission policy) definisce ciò che la funzione può fare. Il runtime inserisce automaticamente le credenziali STS a breve termine in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY e AWS_SESSION_TOKEN. Non incorporare mai le chiavi di accesso di un utente IAM nelle variabili d’ambiente o nel codice: sono statiche, individuabili nel codice sorgente trapelato o negli export di CloudTrail, devono essere ruotate manualmente e aggirano l’intero modello di credenziali temporanee.
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
Le variabili d’ambiente che contengono configurazioni sensibili dovrebbero essere crittografate con una chiave KMS gestita dal cliente, utilizzando gli “helper per la crittografia in transito” in modo che la console esegua la crittografia lato client. Decrittografare una sola volta in fase di INIT e memorizzare il testo in chiaro in una cache a livello di modulo, altrimenti ogni invocazione comporterà il costo di una chiamata API a KMS.
Di default, una funzione viene eseguita in un VPC gestito da AWS con accesso a internet in uscita senza restrizioni. Collegala al tuo VPC solo quando deve raggiungere risorse residenti nel VPC (RDS in sottoreti private, ElastiCache, on-premise tramite Direct Connect). Lambda collegherà quindi delle ENI Hyperplane nelle sottoreti specificate ed erediterà le loro regole di routing.
La trappola classica è posizionare una Lambda in una sottorete privata senza una rotta per il traffico verso i servizi AWS, se non attraverso un NAT gateway — o peggio, un’istanza NAT autogestita. Le istanze NAT si saturano su una singola NIC, sono un SPOF (Single Point of Failure) e vengono fatturate per GB. Anche i NAT gateway hanno un costo per GB e non sono necessari per il traffico verso i servizi AWS. Il pattern corretto è:
- Endpoint VPC di tipo Gateway per S3 e DynamoDB (gratuiti)
- Endpoint di tipo Interfaccia (PrivateLink) per KMS, Secrets Manager, SQS, SNS, STS e simili
Questo mantiene il traffico sul backbone di AWS, rimuove i limiti di throughput del NAT e previene fatture a sorpresa per il traffico in uscita (egress). Nota che le ENI di Lambda non ricevono mai un IP pubblico; posizionare la funzione in una sottorete “pubblica” non le dà accesso a internet — è comunque necessario un NAT gateway in una sottorete pubblica separata con una rotta appropriata.
Amazon API Gateway: Tipi di Endpoint, Autorizzazione e Delivery
API Gateway offre tre tipi di API:
| Caratteristica | REST API | HTTP API | WebSocket |
|---|---|---|---|
| Latenza | Più alta | ~60% più bassa | Stateful, bidirezionale |
| Costo | Più alto | ~70% più economico | Per messaggio |
| Piani di utilizzo / Chiavi API | Sì | No | No |
| Trasformazioni richiesta/risposta (VTL) | Sì | Limitate | — |
| Authorizer JWT | Tramite Lambda | Nativo | Tramite Lambda |
| WAF | Sì | No (usare CloudFront come frontend) | Sì |
Scegli le REST API quando hai bisogno di chiavi API e piani di utilizzo, validazione delle richieste tramite JSON Schema, template di mapping VTL, authorizer Cognito con scope per metodo o WAF; scegli le HTTP API per pattern di proxy leggeri autenticati con JWT dove il costo e la latenza sono più importanti.
I tipi di endpoint determinano dove l’API viene esposta:
- Edge-optimized: esposta tramite una distribuzione CloudFront gestita da Gateway; la terminazione TLS avviene al POP più vicino. Ideale per client distribuiti a livello globale.
- Regional: esposta da una singola Regione senza CloudFront. Ideale quando i client si trovano nella stessa Regione, quando vuoi usare una tua distribuzione CloudFront o quando usi il routing basato sulla latenza di Route 53 su più Regioni.
- Private: raggiungibile solo tramite un endpoint VPC di tipo interfaccia. Ideale per microservizi interni che non devono transitare sulla rete internet pubblica.
Selezionare “edge-optimized” per un’API interna a una singola Regione aggiunge un hop CloudFront non necessario e una propagazione più lenta dei deployment. Selezionare “regional” per un’API pubblica consumata globalmente forza ogni richiesta ad attraversare internet pubblico per raggiungere una singola Regione.
I domini personalizzati richiedono certificati ACM con una posizione che dipende dal tipo di endpoint: us-east-1 per gli endpoint edge-optimized (CloudFront è globale e termina il TLS lì); la Regione stessa dell’API per gli endpoint regional. Confondere questo aspetto è un errore di configurazione comune. Le mappature del percorso di base (base path mapping) consentono a un singolo dominio di multiplexare più API (/orders → API ordini, /users → API utenti).
L’autorizzazione ha quattro modelli, e andare oltre i meccanismi integrati è un classico anti-pattern:
| Meccanismo | Quando usarlo |
|---|---|
| Autorizzazione IAM | I chiamanti sono principal AWS in grado di firmare con SigV4 (altri servizi, SDK, cross-account) |
| Authorizer di pool di utenti Cognito | Gli utenti si autenticano con un pool di utenti Cognito; il Gateway valida il JWT |
| Authorizer Lambda | Token non standard, IdP di terze parti senza OIDC, logica complessa per richiesta |
| Chiavi API + piani di utilizzo | Misurazione, throttling, quote — mai usati come autenticazione |
Un authorizer Lambda personalizzato aggiunge un’invocazione extra per ogni richiesta (o per la durata della cache TTL), un’altra funzione da manutenere e monitorare, e un percorso di codice dove bug nella validazione della firma possono silenziosamente consentire l’accesso. Usalo solo quando i meccanismi integrati non possono davvero soddisfare il requisito.
L’integrazione proxy con Lambda è il pattern di default: l’intera richiesta viene passata come un evento e la funzione deve restituire una risposta con la struttura (envelope) prevista:
{
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": "{\"orderId\":\"abc123\"}",
"isBase64Encoded": false
}
Per un’ingestion di tipo fire-and-forget a TPS (transazioni al secondo) molto elevato, usa l’integrazione diretta con un servizio AWS di Gateway per inviare i dati direttamente a SQS o Kinesis, saltando completamente l’hop della Lambda. Questo elimina i cold start sul percorso di scrittura e disaccoppia la velocità di ingestion dalla capacità di elaborazione.
Per rollout sicuri, usa i deployment canary su uno stage: una percentuale del traffico viene indirizzata al nuovo deployment mentre la maggior parte rimane sulla versione stabile; le metriche CloudWatch specifiche per la versione canary guidano la decisione di promuovere o effettuare il rollback.
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
Per un semplice webhook dove la complessità di API Gateway è eccessiva (es. callback Slack single-tenant, gestore di push di GitHub), gli URL di funzione Lambda forniscono un endpoint HTTPS dedicato direttamente sulla funzione. Proteggili con AuthType: AWS_IAM quando i chiamanti sono principal AWS; se è NONE, è obbligatorio validare la firma della richiesta all’interno della funzione. Un URL di funzione con AuthType: NONE e nessuna verifica all’interno della funzione è un endpoint di calcolo anonimo su internet pubblico.
Tipi di Invocazione, Tentativi e Idempotenza
Le sorgenti di eventi si dividono in due categorie con comportamenti molto diversi. Le sorgenti di tipo push (API Gateway, ALB, S3, SNS, EventBridge, Cognito) invocano Lambda direttamente e richiedono una policy basata su risorse AWS::Lambda::Permission che conceda lambda:InvokeFunction con il Principal corretto (ad es., events.amazonaws.com) e il SourceArn. Senza di essa, la regola corrisponde agli eventi ma ogni invocazione viene rifiutata silenziosamente — la funzione non viene mai eseguita e i fallimenti appaiono solo in CloudTrail. Questo è distinto dal ruolo di esecuzione, che governa ciò che la funzione può fare, non chi può chiamarla.
Le sorgenti di tipo poll (SQS, Kinesis, DynamoDB Streams, MSK) vengono lette dal servizio Lambda tramite una mappatura della sorgente di eventi — non è necessaria alcuna policy basata su risorse, ma il ruolo di esecuzione deve concedere i permessi di lettura.
I tipi di invocazione determinano ulteriormente il comportamento dei tentativi (retry):
- Sincrona (API Gateway, ALB, SDK
RequestResponse): gli errori vengono restituiti immediatamente; il chiamante è responsabile dei tentativi. - Asincrona (S3, SNS, EventBridge): Lambda mette gli eventi in un buffer su una coda interna e, in caso di fallimento, ritenta due volte con dei ritardi, per poi inviarli a una DLQ o a una destinazione in caso di fallimento.
- Mappatura sorgente eventi SQS: SQS continua a riconsegnare il messaggio finché il
maxReceiveCountnon ne attiva lo spostamento nella DLQ della coda di origine. - Kinesis / DynamoDB Streams: ritenta un singolo batch di uno shard fino al successo o alla scadenza, bloccando lo shard nel frattempo — un batch bloccato arresta tutta l’elaborazione a valle per quella partizione.
Poiché i tentativi sono integrati in ogni livello, l’idempotenza è obbligatoria, non opzionale. La scadenza di un visibility timeout di SQS durante una scrittura lenta o un tentativo asincrono dopo un errore 5xx a valle produrranno duplicati. Il pattern canonico utilizza una chiave deterministica e una scrittura condizionale su 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)
Il decoratore @idempotent di AWS Lambda Powertools implementa esattamente questo pattern utilizzando DynamoDB come backend.
Disaccoppiamento con SQS e SNS
Collegare sorgenti push ad alto fan-out direttamente a Lambda è fragile. Le notifiche di eventi S3, ad esempio, sono sincrone per ogni evento e soggette al limite massimo di concorrenza di Lambda; se le invocazioni superano la concorrenza disponibile durante un caricamento a raffica (una campagna di marketing che rilascia migliaia di documenti in pochi secondi), la finestra di tentativi di S3 è breve e gli eventi possono di fatto essere persi. Il rimedio è un buffer:
| Pattern | Quando usarlo |
|---|---|
| S3 → Lambda diretto | Frequenza di eventi bassa e prevedibile; elaborazione idempotente |
| S3 → SQS → Lambda | Carichi di lavoro a raffica; necessità di retry/DLQ; rate limit a valle |
| S3 → SNS → più SQS | Fan-out verso più consumer indipendenti |
| S3 → EventBridge → molti target | Routing tra account; filtraggio basato sul contenuto |
SNS è un sistema pub/sub: una pubblicazione, molti sottoscrittori (SQS, Lambda, HTTPS, email). Il filtraggio dei messaggi è basato sugli attributi. SQS è una coda point-to-point durevole che conserva i messaggi fino a 14 giorni. La combinazione più comune e robusta è il fan-out SNS → SQS, che fornisce a ogni consumer la propria coda bufferizzata per scalabilità e riesecuzione indipendenti.
Per un ordinamento rigoroso (ad es., ordini per cliente elaborati in sequenza), usare una coda SQS FIFO con MessageGroupId impostato sulla chiave di ordinamento. I messaggi all’interno di un gruppo vengono consegnati in ordine; gruppi diversi vengono elaborati in parallelo. Le code SQS standard offrono solo un ordinamento best-effort.
Il pattern canonico di ingestion disaccoppiata utilizza l’integrazione diretta di API Gateway con SQS per assorbire i picchi e un processore con rate limiting:
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
La concorrenza riservata è deliberata — limita la velocità con cui il database riceve le scritture, così che la coda (e non RDS) assorba il picco. I messaggi falliti vengono instradati a una DLQ dopo maxReceiveCount per un’ispezione offline.
EventBridge: Regole, Input Transformation e API Destination
EventBridge è un event bus consapevole dello schema con matching avanzato di pattern JSON, sorgenti di partner SaaS, registro degli schemi e funzionalità di archiviazione/replay. Le regole (rule) effettuano il matching sui pattern degli eventi e fanno fan-out verso più di 30 tipi di target (Lambda, Step Functions, SQS, Kinesis, ECS, Firehose), con filtraggio basato sul contenuto del corpo del messaggio — non solo sugli attributi come in SNS:
{
"source": ["tenant.energy"],
"detail-type": ["UsageReported"],
"detail": { "kWh": [{ "numeric": [">", 100] }] }
}
Una regola può avere fino a cinque target, ognuno dei quali riceve l’evento grezzo o un sottoinsieme trasformato. Gli Input transformer impongono un disaccoppiamento debole: un InputPathsMap estrae percorsi JSON dall’evento e un InputTemplate li rimodella esattamente in ciò che il target si aspetta:
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>}
Ogni Lambda di validazione riceve solo ciò di cui ha bisogno; il validatore dell’indirizzo non vede mai il token della carta di credito. Questo è decisamente superiore a una Lambda monolitica che riceve l’intero evento ed esegue diramazioni al suo interno — un monolite concentra i permessi IAM (un unico ruolo deve possedere tutti i permessi a valle), aumenta il raggio d’impatto (blast radius) di un bug, accoppia la cadenza dei deployment, impedisce di ottimizzare memoria/timeout per singola responsabilità e costringe l’intera funzione a scalare sulla base della frequenza del ramo più trafficato.
Le API destination invertono la direzione: EventBridge chiama un endpoint HTTPS esterno. Abbinate a una connessione (connection) che memorizza credenziali di tipo Basic, API-key o OAuth in Secrets Manager, questo è il modo serverless per notificare un SaaS di terze parti quando, ad esempio, un job di AWS Batch ha successo — senza bisogno di Lambda. EventBridge cattura l’evento di cambio di stato, una regola fa matching su JobSucceeded e il target di tipo API destination effettua una POST verso il vendor con le credenziali iniettate dalla connessione.
Le regole pianificate (Scheduled rule) (con espressioni cron/rate) o il più recente EventBridge Scheduler sostituiscono le istanze EC2 di heartbeat per task periodici — report notturni, aggiornamento orario della cache.
Scegliere EventBridge invece di SNS quando il filtraggio si basa sul contenuto del corpo del messaggio (non solo sugli attributi), quando nuovi consumer devono potersi collegare in un secondo momento senza modifiche al producer, o quando il routing si estende a più account o sorgenti SaaS. Scegliere SNS quando il fan-out è una semplice notifica filtrata per attributi a un insieme stabile di sottoscrittori.
Step Functions: Orchestrazione e Map Distribuita
Quando un flusso di lavoro ha più di un paio di passaggi, diramazioni, tentativi di riesecuzione, approvazioni umane o lunghe attese, incorporare tale logica in una catena di Lambda diventa ingestibile. Step Functions esternalizza la macchina a stati in Amazon States Language.
- Flussi di lavoro Standard: fino a un anno, semantica exactly-once, cronologia completa delle esecuzioni,
.waitForTaskTokenper blocchi umani/esterni. Evasione ordini, ETL, flussi di approvazione. - Flussi di lavoro Express: fino a cinque minuti, semantica at-least-once, volumi elevati, costo per esecuzione ridotto. Orchestrazioni sincrone brevi dietro API Gateway; trasformazioni per IoT e streaming.
Ogni task dovrebbe dichiarare esplicitamente Retry e 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"
}
Gli stati Parallel e Map eseguono rami in modo concorrente e aggregano i risultati — una soluzione ideale per sistemi di gestione ordini con validatori indipendenti (indirizzo, inventario, pagamento). Lo stato tra i passaggi fluisce attraverso il documento JSON dell’esecuzione, eliminando la necessità di un database condiviso usato unicamente per il coordinamento del flusso di lavoro.
Il pattern .waitForTaskToken mette in pausa l’esecuzione finché un attore esterno non chiama SendTaskSuccess con il token — la risposta canonica quando un flusso di lavoro si estende tra Lambda, EC2, container, sistemi on-premise e richiede approvazione manuale con un overhead operativo minimo:
"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 estende lo stato Map standard per elaborare fino a 10.000 esecuzioni figlie parallele e può iterare direttamente su oggetti in un bucket S3 o su righe di un file CSV/JSONL, con batching, checkpointing e tolleranza ai guasti automatici. Per migliaia di oggetti S3 semistrutturati, è l’opzione operativamente più efficiente: basta puntarlo a un prefisso, definire il task per singolo elemento, e Step Functions gestisce il fan-out, MaxConcurrency, i tentativi di riesecuzione e l’aggregazione dei risultati:
{
"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 } }
}
}
Ricostruire la stessa logica su SQS o EventBridge richiederebbe una gestione personalizzata per il completamento, i tentativi e l’assemblaggio dei risultati.
Step Functions vs. EventBridge: Step Functions è la scelta giusta quando tu controlli la sequenza e il risultato — stato, diramazioni, tentativi, approvazioni. EventBridge è la scelta giusta quando i producer non sanno o non si preoccupano di chi consuma gli eventi, e i consumer si collegano in modo indipendente.
Notifiche di Eventi S3
Per l’elaborazione quasi in tempo reale degli upload, configura le notifiche di eventi S3 su s3:ObjectCreated:* (o una variante specifica come Put, Post, CompleteMultipartUpload) con una Lambda come destinazione — tenendo conto delle avvertenze sui picchi di traffico (burst) menzionate sopra. Misure di protezione:
- Imposta la concorrenza riservata (reserved concurrency) per proteggere un database a valle.
- Abilita una DLQ o una destinazione on-failure per i messaggi non elaborabili (poison message).
- Instrada gli eventi S3 attraverso EventBridge quando più consumer necessitano degli stessi eventi. Le configurazioni di notifica diretta di S3 sono limitate in numero ed espressività; il fan-out di EventBridge con trasformatori di input per destinazione (input transformer) scala molto meglio.
Streaming: Kinesis Data Streams vs. Firehose
Kinesis Data Streams (KDS) è un log partizionato (sharded), ordinato e riproducibile con una conservazione (retention) da 24 ore a 365 giorni. L’ordine è preservato per shard, basato sulla chiave di partizione — un aspetto critico per l’aggregazione per dispositivo o per tenant. Più consumer possono leggere in modo indipendente (enhanced fan-out per un throughput isolato per consumer). Scegli KDS quando hai bisogno di riproduzione ordinata (replay), più consumer indipendenti o un throughput elevato per shard.
Kinesis Data Firehose è un flusso di consegna completamente gestito verso S3, Redshift, OpenSearch o Splunk, con buffering integrato (60 s o 1–128 MB), trasformazione opzionale tramite Lambda, compressione (GZIP, Snappy) e conversione in formato Parquet/ORC. Non ci sono shard da gestire. Firehose è la scelta a basso impatto operativo quando non sono necessari consumer personalizzati o la riproduzione dei dati, ma solo l’atterraggio di dati quasi in tempo reale.
La pipeline canonica per l’analisi quasi in tempo reale è producer → KDS → Firehose → S3 (Parquet) → Athena/QuickSight, con un arricchimento opzionale tramite Lambda in Firehose.
AWS Transfer Family per SFTP Gestito
Quando i partner richiedono l’uso di SFTP, FTPS o FTP per trasferire file da o verso S3 o EFS, AWS Transfer Family fornisce un endpoint gestito e multi-AZ che parla il protocollo già utilizzato dai client. L’autenticazione supporta utenti gestiti dal servizio, chiavi SSH o IdP personalizzati tramite API Gateway/Lambda. I file atterrano direttamente in S3 con SSE e le policy di ciclo di vita applicate; le policy IAM di scope-down limitano ogni utente a un prefisso specifico.
Costruire una soluzione simile su EC2 richiederebbe l’hardening di OpenSSH, l’applicazione di patch, l’alta disponibilità (HA) tra AZ, la rotazione delle chiavi e l’invio dei log — tutte attività che Transfer Family gestisce al posto tuo. Sceglilo ogni volta che il requisito è “i partner ci inviano file tramite SFTP” e vuoi che i file arrivino in S3 con il minimo overhead operativo.
Comporre un Pattern Serverless Canonico
Un design di ingestione a basso overhead per metriche orarie per-tenant: i sensori inviano una richiesta POST a API Gateway (HTTP API, regional) → una Lambda valida e pubblica su EventBridge → delle regole instradano l’evento a una Lambda che scrive su DynamoDB (ID del tenant come chiave di partizione, bucket orario come chiave di ordinamento) e, in parallelo, a Firehose → S3 (Parquet) per l’analisi. Nuovi consumer si collegano come regole aggiuntive di EventBridge senza modificare i producer — un requisito di estensibilità che il solo SNS non soddisferebbe in modo altrettanto pulito. Laddove il throughput sostenuto e l’ordinamento sono importanti (riconciliazione della fatturazione, flussi di eventi finanziari), sostituisci EventBridge con Kinesis Data Streams e utilizza l’enhanced fan-out per i consumer indipendenti.
Catalogo delle trappole: perché i pattern errati più comuni falliscono
Istanza/gateway NAT per il traffico verso servizi AWS da Lambda in subnet private. Le istanze NAT legano il throughput alla NIC di una singola EC2 e sono un SPOF; i gateway NAT fatturano per GB. Entrambi non sono necessari per destinazioni verso servizi AWS. Usare endpoint gateway per S3/DynamoDB, endpoint di interfaccia per tutto il resto.
Ignorare i cold start su API sensibili alla latenza. La modalità on-demand esegue il provisioning dei container solo quando arriva una richiesta, quindi i picchi di traffico (burst) non rispettano gli SLA. Dimensionare la provisioned concurrency sul p95 dei burst; usare SnapStart per i carichi di lavoro Java senza SLA stringenti.
Lambda monolitica che riceve un evento completo e si dirama internamente. Viola il principio del privilegio minimo (un singolo ruolo detiene tutte le permissioni a valle), accoppia la cadenza di deployment, impedisce l’ottimizzazione per singola responsabilità e scala l’intera funzione al ritmo del ramo più rumoroso. Suddividere per responsabilità; collegare con EventBridge o Step Functions.
Connessioni dirette al DB da molte Lambda. Il numero totale di connessioni eguaglia le invocazioni concorrenti perché i container non condividono i pool. Risolvere con RDS Proxy (pooling), reserved concurrency (rate cap) o SQS (buffering).
Lambda sincrona per l’ingestion di dati a raffica (bursty). Il superamento della concorrenza restituisce 429 ad API Gateway e 5xx ai client; le notifiche dirette da S3 durante i picchi di traffico scartano silenziosamente gli eventi. Inserire SQS o usare l’integrazione diretta Gateway → SQS.
URL di funzione Lambda pubblici con AuthType: NONE e nessuna validazione della firma all’interno della funzione. Capacità di calcolo anonima su internet pubblico. Usare AWS_IAM per chiamanti AWS o validare le firme per webhook di terze parti.
Authorizer Lambda personalizzato dove ne basterebbe uno integrato. Aggiunge latenza, un’altra funzione da manutenere e un percorso di codice in cui bug nel controllo della firma consentono silenziosamente l’accesso. Preferire l’autorizzazione IAM per i principal AWS; l’authorizer Cognito per le user pool.
Regione errata del certificato ACM per i domini personalizzati. Gli endpoint edge-optimized richiedono il certificato in us-east-1; gli endpoint regionali nella stessa Regione dell’API.
Mancanza di AWS::Lambda::Permission per le sorgenti di tipo push (EventBridge, S3, SNS). Gli eventi corrispondono alle regole ma le invocazioni vengono negate silenziosamente. È una cosa distinta dal ruolo di esecuzione: questo regola chi può chiamare la funzione, non cosa la funzione può fare.
Mancanza di idempotenza. Ogni livello esegue dei tentativi (retry): invocazioni asincrone, riconsegna di SQS alla scadenza del visibility-timeout, retry dei batch di Kinesis. Senza una chiave di deduplicazione deterministica e una scrittura condizionale, i duplicati sono inevitabili.
← Container e Orchestrazione · Tutti i domini · Storage e Ciclo di Vita dei Dati →
Esercitati su queste domande → · Pratica cronometrata su 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.
Supera l'esame →