Amazon SQS: 메시지 대기열

Amazon Simple Queue Service(SQS) 관리되는 메시지 대기열입니다. 생산자는 대기열에 메시지를 보내고 소비자는 폴링을 통해 메시지를 검색합니다. 기본 의미론은 다음과 같습니다. 지점 간: 대기열에 있는 메시지 한 번에 한 명의 소비자가 읽습니다(먼저 가져가는 사람이 다른 소비자에게 "숨깁니다"). 를 통해 공개 시간 초과).

표준 대기열과 FIFO 대기열

SQS는 두 가지 모드를 제공합니다.

  • 표준 대기열: 무제한 처리량, 최소 1회 배송 (중복 가능), 최선을 다해 주문합니다(보장되지 않음). 대용량 사용 사례의 경우 소비자 측에서 중복을 관리할 수 있습니다.
  • FIFO 대기열: 딱 한 번만 전달(자동 중복 제거를 통해) MessageDeduplicationId), 정렬 보장 메시지 그룹, 처리량은 요청당 3,000msg/s로 제한됩니다. (배칭 사용). 주문 및 복제가 중요한 사용 사례(예: 금융 거래)에 적합합니다.
// 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 소비자: 폴링 및 가시성 시간 초과

// 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: 팬아웃 게시/구독

아마존 단순 알림 서비스(SNS) 패턴을 구현합니다 게시/구독: 생산자가 메시지를 게시합니다. SNS 주제, SNS는 모든 사람에게 동시에 전달합니다. 회원들 (구독자). SNS 주제에는 수천 명의 구독자가 있을 수 있습니다. Lambda, SQS, HTTP 엔드포인트, 이메일, SMS, 모바일 푸시.

SNS + SQS 패턴(SNS 팬아웃)은 가장 일반적인 패턴 중 하나입니다. AWS 아키텍처: SNS는 동일한 이벤트를 여러 SQS 대기열에 병렬로 전달합니다. 각 대기열은 서로 다른 소비자(다른 마이크로서비스)를 제공합니다.

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

완전한 비교

특성 SQS SNS 이벤트브리지
모델 지점 간(큐) 팬아웃(게시/구독) 콘텐츠 기반 라우팅
수신자 한 번에 1명의 소비자 모두 병렬로 등록됨 규칙별 대상(1-5)
필터 아무도 (모든 것을 가져간다) 메시지 속성(제한됨) JSON 필드의 이벤트 패턴
정렬 보장된 FIFO(FIFO 대기열) No No
중복 제거 예(FIFO 대기열) No No
최대. 처리량 무제한(표준) 제한 없는 지역당 초당 1,000만 개의 이벤트
숨어 있음 밀리초(폴링) < 1초(푸시) < 500ms
스키마 레지스트리 No No 예(통합)
이벤트 보관/재생 No No Si
파트너 통합 SaaS No No 예(35개 이상의 파트너)
백만 메시지당 비용 ~$0.40 ~$0.50 ~$1.00

결정 가이드: 언제 어느 것을 사용할 것인가?

다음과 같은 경우 SQS를 사용하세요.

  • 당신은 로드 밸런싱 동일한 유형의 여러 소비자 간(예: 동일한 대기열을 처리하는 5개의 Lambda)
  • 메시지는 정교해야 한다 정확히 한 번 (FIFO 대기열)
  • 당신은 배압: 소비자가 느려지면 대기열에 메시지가 쌓입니다.
  • 당신은 배송 지연 (단일 메시지당 최대 15분)
  • 기존 대기열 모델을 사용하는 레거시 시스템을 통합하고 있습니다.

다음과 같은 경우 SNS를 사용하세요.

  • 동일한 이벤트가 도달해야 합니다. 여러 시스템을 병렬로 (팬아웃 패턴)
  • 당신은 보내야합니다 알림 (이메일, SMS, 모바일 푸시, HTTP 웹훅)
  • SNS + SQS를 결합하여 각 소비자에 대한 팬아웃 및 버퍼링 기능 제공

다음과 같은 경우 EventBridge를 사용하십시오.

  • 당신은 지능형 라우팅 페이로드의 내용을 기반으로
  • 와 통합하다 AWS 서비스 (EC2, S3, RDS) 또는 SaaS 파트너 (Zendesk, Shopify, 스트라이프)
  • 당신은 그것을 원한다 스키마 레지스트리 및 계획 거버넌스
  • 당신은 이벤트 보관 및 재생
  • 빌드 기업 이벤트 버스 중앙 집중식 라우팅

SNS + SQS + 람다 패턴: 완전한 스택

# 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: 두 세계의 최고

많은 아키텍처에서 EventBridge와 SQS는 상호 보완적입니다. EventBridge는 지능형 라우팅을 수행하고 SQS는 버퍼링 및 로드 밸런싱을 수행합니다. 이벤트가 EventBridge에 도착하고 적절한 SQS 대기열로 라우팅됩니다. 소비자 Lambda는 재시도 및 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
  }
}

시리즈의 다음 단계

  • 8조 - 배달 못한 편지 대기열 및 탄력성: 올바른 구성 SQS, SNS 및 EventBridge에 대한 DLQ는 메시지 손실을 방지하는 데 매우 중요합니다. 공개 상태 제한 시간, maxReceiveCount 및 재처리 패턴.

다른 시리즈와의 연계

  • AWS EventBridge(6조): 이벤트 패턴에 대한 심층 분석, 스키마 레지스트리 및 이벤트 아카이브, 모든 EventBridge 구성 세부 정보.
  • 아파치 카프카(시리즈 38): 온프레미스 또는 매우 높은 처리량 사용 사례 (>1천만 msg/s), Kafka는 SQS/SNS보다 우수합니다. 선택은 클라우드 제공업체에 따라 다릅니다. 처리량 및 이벤트 재생의 필요성에 따라 결정됩니다.