SQS versus SNS versus EventBridge: wanneer moet u welke AWS-service gebruiken?
Drie AWS-services, drie verschillende filosofieën. SQS is een wachtrij: het garandeert dat elk bericht wordt verwerkt door één en slechts één consument. SNS het is een fan-out pub/sub: bezorg het hetzelfde bericht tegelijkertijd naar alle abonnees. EventBridge het is een slimme router: routeert evenementen op basis van inhoud. Het kiezen van de verkeerde dienst betekent fragiele architecturen, hoge kosten en onverwacht verloren of dubbele berichten.
Amazon SQS: de berichtenwachtrij
Amazon Simple Queue-service (SQS) is een beheerde berichtenwachtrij: producenten sturen berichten naar de wachtrij, consumenten halen ze op via polling. De fundamentele semantiek is punt-tot-punt: Een bericht in de wachtrij het wordt door één consument tegelijk gelezen (de eerste die het pakt, "verbergt" het voor de anderen via de time-out voor zichtbaarheid).
Standaardwachtrij versus FIFO-wachtrij
SQS biedt twee modi:
- Standaard wachtrij: Onbeperkte doorvoer, minimaal één keer bezorgen (duplicaten mogelijk), best-effort-bestelling (niet gegarandeerd). Voor gebruiksscenario's met grote volumes waar duplicaten beheersbaar zijn aan de consumentenzijde.
-
FIFO-wachtrij: exact éénmalige levering (automatische ontdubbeling via
MessageDeduplicationId), gegarandeerde sortering voor berichten groep, doorvoer beperkt tot 3.000 msg/s per verzoek (met batching). Voor gebruiksscenario's waarbij bestellen en duplicaten van cruciaal belang zijn (bijvoorbeeld financiële transacties).
// SQS Producer Java con AWS SDK v2
import software.amazon.awssdk.services.sqs.*;
import software.amazon.awssdk.services.sqs.model.*;
public class SqsProducer {
private final SqsClient sqsClient = SqsClient.builder()
.region(Region.EU_WEST_1)
.build();
// Standard Queue: invio semplice
public void sendToStandardQueue(String queueUrl, String messageBody) {
SendMessageResponse response = sqsClient.sendMessage(
SendMessageRequest.builder()
.queueUrl(queueUrl)
.messageBody(messageBody)
// Delay di consegna (0-900 secondi)
.delaySeconds(0)
// Attributi del messaggio per filtraggio (SNS subscription filter)
.messageAttributes(Map.of(
"eventType", MessageAttributeValue.builder()
.stringValue("OrdineCreato")
.dataType("String")
.build()
))
.build()
);
System.out.println("Messaggio inviato: " + response.messageId());
}
// FIFO Queue: richiede MessageGroupId e MessageDeduplicationId
public void sendToFifoQueue(String queueUrl, String ordineId, String payload) {
sqsClient.sendMessage(
SendMessageRequest.builder()
.queueUrl(queueUrl) // url deve terminare in .fifo
.messageBody(payload)
// Group: tutti i messaggi dello stesso ordine vengono elaborati in ordine
.messageGroupId("ordine-" + ordineId)
// Deduplication: prevenzione duplicati (valido 5 minuti)
.messageDeduplicationId(ordineId + "-" + System.currentTimeMillis())
.build()
);
}
}
SQS-consument: time-out voor polling en zichtbaarheid
// SQS Consumer con Long Polling e gestione DLQ
public class SqsConsumer {
private final SqsClient sqsClient = SqsClient.builder()
.region(Region.EU_WEST_1).build();
public void poll(String queueUrl) {
while (true) {
// Long polling: aspetta fino a 20 secondi per nuovi messaggi
// Riduce le chiamate vuote (e i costi) rispetto al short polling
ReceiveMessageResponse response = sqsClient.receiveMessage(
ReceiveMessageRequest.builder()
.queueUrl(queueUrl)
.maxNumberOfMessages(10) // max 10 per chiamata
.waitTimeSeconds(20) // long polling
.visibilityTimeout(30) // 30s per elaborare
.messageAttributeNames("All")
.build()
);
for (Message message : response.messages()) {
try {
processMessage(message.body());
// Successo: elimina dalla coda
sqsClient.deleteMessage(
DeleteMessageRequest.builder()
.queueUrl(queueUrl)
.receiptHandle(message.receiptHandle())
.build()
);
} catch (Exception e) {
// Non eliminare: SQS rirenderà visibile il messaggio
// dopo il visibility timeout. Dopo maxReceiveCount tentativi
// finisce nella DLQ configurata
System.err.println("Errore, il messaggio tornera visibile: " + e.getMessage());
}
}
}
}
}
Amazon SNS: Fan-Out Pub/Sub
Amazon Simple Notification Service (SNS) implementa il pattern publish/subscribe: un producer pubblica un messaggio su un topic SNS, e SNS lo consegna in parallelo a tutti gli iscritti (subscriber). Un topic SNS può avere migliaia di sottoscrittori: Lambda, SQS, HTTP endpoint, email, SMS, mobile push.
Il pattern SNS + SQS (SNS Fan-Out) è uno degli schemi più comuni in architetture AWS: SNS consegna lo stesso evento a più code SQS in parallelo, e ogni coda serve un consumer diverso (un microservizio diverso).
# SNS + SQS Fan-Out: un evento raggiunge 3 servizi in parallelo
# Struttura:
# SNS Topic "ordini-topic"
# |--- SQS "inventario-queue" --- Lambda inventario
# |--- SQS "pagamenti-queue" --- Lambda pagamenti
# |--- SQS "notifiche-queue" --- Lambda notifiche email
# Terraform
resource "aws_sns_topic" "ordini" {
name = "ordini-topic"
}
resource "aws_sqs_queue" "inventario" {
name = "inventario-queue"
visibility_timeout_seconds = 60
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.inventario_dlq.arn
maxReceiveCount = 3
})
}
resource "aws_sns_topic_subscription" "ordini_inventario" {
topic_arn = aws_sns_topic.ordini.arn
protocol = "sqs"
endpoint = aws_sqs_queue.inventario.arn
# Filter policy: questa subscription riceve SOLO OrdineCreato
filter_policy = jsonencode({
eventType = ["OrdineCreato"]
})
}
// SNS Publisher Java
import software.amazon.awssdk.services.sns.*;
import software.amazon.awssdk.services.sns.model.*;
public class SnsPublisher {
private final SnsClient snsClient = SnsClient.builder()
.region(Region.EU_WEST_1).build();
public void publishOrdineCreato(String topicArn, OrdineCreato event) throws Exception {
String messageJson = new ObjectMapper().writeValueAsString(event);
PublishResponse response = snsClient.publish(
PublishRequest.builder()
.topicArn(topicArn)
.message(messageJson)
// Message attributes per subscription filter
.messageAttributes(Map.of(
"eventType", MessageAttributeValue.builder()
.stringValue("OrdineCreato")
.dataType("String")
.build()
))
// Subject visibile solo nelle email
.subject("Nuovo ordine: " + event.getOrdineId())
.build()
);
System.out.println("SNS Message ID: " + response.messageId());
}
}
Il Confronto Completo
| Caratteristica | SQS | SNS | EventBridge |
|---|---|---|---|
| Modello | Point-to-point (coda) | Fan-out (pub/sub) | Content-based routing |
| Destinatari | 1 consumer alla volta | Tutti gli iscritti in parallelo | Target per regola (1-5) |
| Filtro | Nessuno (prende tutto) | Message attributes (limitato) | Event pattern su qualsiasi campo JSON |
| Ordinamento | FIFO garantito (FIFO queue) | No | No |
| Deduplication | Si (FIFO queue) | No | No |
| Throughput max | Illimitato (Standard) | Illimitato | 10 milioni eventi/s per region |
| Latenza | Milliseconds (polling) | < 1s (push) | < 500ms |
| Schema Registry | No | No | Si (integrato) |
| Event Archive/Replay | No | No | Si |
| Partner integration SaaS | No | No | Si (35+ partner) |
| Costo per milione messaggi | ~$0.40 | ~$0.50 | ~$1.00 |
Beslissingsgids: wanneer moet u welke gebruiken?
Gebruik SQS wanneer:
- Dat moet taakverdeling tussen meerdere consumenten van hetzelfde type (bijvoorbeeld 5 Lambda's die dezelfde wachtrij verwerken)
- De boodschap moet worden uitgewerkt precies één keer (FIFO-wachtrij)
- Dat moet tegendruk: Berichten stapelen zich op in de wachtrij als consumenten traag zijn
- Wil je de levering vertraging (tot 15 minuten per enkel bericht)
- Je integreert legacy-systemen die het traditionele wachtrijmodel gebruiken
Gebruik SNS wanneer:
- Dezelfde gebeurtenis moet bereiken meerdere systemen parallel (uitwaaierpatroon)
- Je moet sturen meldingen (e-mail, sms, mobiele push, HTTP-webhook)
- Combineer SNS + SQS om voor elke consument zowel fan-out als buffering te hebben
Gebruik EventBridge wanneer:
- Dat moet intelligente routering op basis van de inhoud van de lading
- Integreer met AWS-diensten (EC2, S3, RDS) of SaaS-partners (Zendesk, Shopify, Streep)
- Je wilt het Schemaregister en schemabeheer
- Dat moet Evenementarchief en herhaling
- Bouw een bedrijfsevenementenbus met gecentraliseerde routering
SNS + SQS + Lambda-patroon: de complete stapel
# Architettura tipica per un microservizio event-driven su AWS
#
# Client HTTP
# |
# v
# API Gateway / Lambda "ordini-api"
# | pubblica su
# v
# SNS Topic "ordini-events"
# |-- filtra "OrdineCreato" --> SQS "inventario-queue" --> Lambda "inventario-handler"
# |-- filtra "OrdineCreato" --> SQS "notifiche-queue" --> Lambda "email-notifier"
# |-- tutti i tipi --> SQS "audit-queue" --> Lambda "audit-logger"
#
# Ogni SQS ha la sua DLQ per messaggi non elaborabili
# Le Lambda usano le SQS come event source (SQS trigger)
# Configurazione Lambda con SQS trigger
resource "aws_lambda_event_source_mapping" "inventario" {
event_source_arn = aws_sqs_queue.inventario.arn
function_name = aws_lambda_function.inventario_handler.arn
batch_size = 10 # processa 10 messaggi alla volta
enabled = true
# Bisection on error: in caso di errore batch, divide il batch a meta
# per identificare il messaggio problematico (evita il poison pill)
bisect_batch_on_function_error = true
# Finestra di aggregazione (utile per batch processing)
maximum_batching_window_in_seconds = 5
}
EventBridge + SQS: het beste van twee werelden
In veel architecturen zijn EventBridge en SQS complementair: EventBridge doet de intelligente routing, SQS doet de buffering en load-balancing. Een evenement arriveert op EventBridge, wordt doorgestuurd naar de juiste SQS-wachtrij, en de consument Lambda verwerkt het vanuit de wachtrij met opnieuw proberen en DLQ.
# EventBridge --> SQS (con transform di input opzionale)
resource "aws_cloudwatch_event_target" "ordini_to_sqs" {
rule = aws_cloudwatch_event_rule.ordini_vip.name
event_bus_name = aws_cloudwatch_event_bus.mioapp.name
arn = aws_sqs_queue.vip_orders.arn
# Input transformer: ristruttura il payload prima di inviarlo a SQS
# Utile per estrarre solo i campi necessari
input_transformer {
input_paths = {
ordineId = "$.detail.ordineId"
clienteId = "$.detail.clienteId"
totale = "$.detail.totale"
}
input_template = <<-EOF
{
"ordineId": "<ordineId>",
"clienteId": "<clienteId>",
"totale": "<totale>",
"processato_da": "eventbridge-vip-rule"
}
EOF
}
}
Volgende stappen in de serie
- Artikel 8 – Dead Letter-wachtrij en veerkracht: de juiste configuratie van DLQ voor SQS, SNS en EventBridge is van cruciaal belang om berichtverlies te voorkomen. Time-out voor zichtbaarheid, maxReceiveCount en het herverwerkingspatroon.
Link met andere series
- AWS EventBridge (artikel 6): diepgaande analyse van gebeurtenispatronen, schemaregister en gebeurtenisarchief, alle EventBridge-configuratiedetails.
- Apache Kafka (serie 38): voor gebruiksscenario's op locatie of met zeer hoge doorvoer (>10 miljoen msg/s), Kafka is superieur aan SQS/SNS. De keuze hangt af van de cloudprovider, door doorvoer en de noodzaak van herhaling van gebeurtenissen.







