Amazon SQS: Fronta zpráv

Amazon Simple Queue Service (SQS) je spravovaná fronta zpráv: producenti posílají zprávy do fronty, spotřebitelé je získávají prostřednictvím dotazování. Základní sémantika je point-to-point: Zpráva ve frontě čte jej vždy jeden spotřebitel (první, kdo si jej vezme, jej „schová“ před ostatními přes časový limit viditelnosti).

Standardní fronta vs fronta FIFO

SQS nabízí dva režimy:

  • Standardní fronta: Neomezená propustnost, alespoň jedno doručení (možné duplikáty), objednání s maximální snahou (není zaručeno). Pro případy velkoobjemového použití kde jsou duplikáty zvládnutelné na straně spotřebitele.
  • Fronta FIFO: přesně jednou doručení (automatická deduplikace přes MessageDeduplicationId), zaručené třídění pro skupina zpráv, propustnost omezena na 3 000 msg/s na požadavek (s dávkováním). Pro případy použití, kdy jsou objednávky a duplikáty kritické (např. finanční transakce).
// 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()
        );
    }
}

Zákazník SQS: Časový limit dotazování a viditelnosti

// 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) implementuje vzor zveřejnit/přihlásit se k odběru: producent odešle zprávu a Témata SNSa SNS je doručuje paralelně všem členové (odběratel). Téma SNS může mít tisíce odběratelů: Lambda, SQS, HTTP endpoint, e-mail, SMS, mobilní push.

Vzor SNS + SQS (SNS Fan Out) je jedním z nejběžnějších vzorů v architekturách AWS: SNS doručuje stejnou událost do více front SQS paralelně, a každá fronta obsluhuje jiného spotřebitele (jinou mikroslužbu).

# 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());
    }
}

Kompletní srovnání

Charakteristický SQS SNS EventBridge
Model Point-to-point (fronta) Fan-out (hospoda/sub) Směrování založené na obsahu
Příjemci 1 spotřebitel najednou Všechny registrovány paralelně Cíle podle pravidla (1-5)
Filtr Nikdo (bere všechno) Atributy zprávy (omezené) Vzor události v libovolném poli JSON
Řazení Garantovaná FIFO (fronta FIFO) No No
Deduplikace Ano (fronta FIFO) No No
Max. propustnost Neomezené (standardní) Neomezený 10 milionů událostí/s na region
Latence Milisekundy (dotazování) < 1 s (stisk) < 500 ms
Registr schémat No No Ano (integrované)
Archiv/Přehrání události No No Si
Integrace partnerů SaaS No No Ano (35 a více partnerů)
Cena za milion zpráv ~0,40 $ ~0,50 $ ~1,00 $

Průvodce rozhodováním: Kdy použít který

Použijte SQS, když:

  • musíte vyvažování zátěže mezi více spotřebiteli stejného typu (např. 5 lambd zpracovávajících stejnou frontu)
  • Zpráva musí být propracovaná přesně jednou (fronta FIFO)
  • musíte protitlak: Zprávy se hromadí ve frontě, když jsou spotřebitelé pomalí
  • Chcete? zpoždění dodávky (až 15 minut na jednu zprávu)
  • Integrujete starší systémy, které používají tradiční model fronty

Použijte SNS, když:

  • Stejná událost musí dosáhnout více systémů paralelně (vějířovitý vzor)
  • Musíte poslat oznámení (e-mail, SMS, mobilní push, HTTP webhook)
  • Kombinujte SNS + SQS, abyste měli pro každého spotřebitele jak rozvětvení, tak vyrovnávací paměť

EventBridge použijte, když:

  • musíte inteligentní směrování na základě obsahu užitečného zatížení
  • Integrujte se s služby AWS (EC2, S3, RDS) popř partneři SaaS (Zendesk, Shopify, Stripe)
  • Chceš to Registr schémat a řízení systému
  • musíte Archiv událostí a přehrávání
  • Sestavte a autobus pro firemní akce s centralizovaným směrováním

Vzor SNS + SQS + Lambda: Kompletní sada

# 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: to nejlepší z obou světů

V mnoha architekturách se EventBridge a SQS doplňují: EventBridge zajišťuje inteligentní směrování, SQS zajišťuje ukládání do vyrovnávací paměti a vyrovnávání zátěže. Událost dorazí na EventBridge, je směrována do příslušné fronty SQS, a spotřebitelská Lambda jej zpracuje z fronty s opakováním a 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
  }
}

Další kroky v sérii

  • Článek 8 – Fronta nedoručených dopisů a odolnost: správná konfigurace DLQ pro SQS, SNS a EventBridge je zásadní pro zamezení ztráty zpráv. Časový limit viditelnosti, maxReceiveCount a vzor opětovného zpracování.

Propojení s ostatními sériemi

  • AWS EventBridge (článek 6): hloubková analýza vzorců událostí, registr schémat a archiv událostí, všechny podrobnosti o konfiguraci EventBridge.
  • Apache Kafka (řada 38): pro místní použití nebo případy použití s velmi vysokou propustností (>10 milionů msg/s), Kafka je lepší než SQS/SNS. Výběr závisí na poskytovateli cloudu, propustností a potřebou přehrávání událostí.