Serverless

Eseményvezérelt feldolgozás megbízhatóan: EventBridge + SQS + Lambda + DLQ, Terraformmal

Az aszinkron feldolgozás akkor jó, ha egyetlen üzenet sem vész el. Felépítünk egy ellenálló pipeline-t: EventBridge routing, SQS pufferrel, Lambda-fogyasztóval és dead-letter queue-val a hibás üzeneteknek — teljes IaC-kal.

Röviden: Az aszinkron feldolgozás akkor jó, ha egyetlen üzenet sem vész el. Felépítünk egy ellenálló pipeline-t: EventBridge routing, SQS pufferrel, Lambda-fogyasztóval és dead-letter queue-val a hibás üzeneteknek — teljes IaC-kal.

Az eseményvezérelt architektúra ígérete egyszerű: a komponensek nem hívogatják közvetlenül egymást, hanem eseményeket adnak közre, és a fogyasztók akkor dolgozzák fel, amikor tudják. A gyakorlatban viszont a naiv megvalósítás csendben üzeneteket veszít: ha a fogyasztó hibázik, az esemény eltűnik. Ebben a cikkben egy megbízható pipeline-t építünk, ahol egyetlen üzenet sem vész el nyomtalanul — EventBridge a routing, SQS a puffer, Lambda a feldolgozó, és egy dead-letter queue fogja fel a menthetetlent.

Miért nem elég az EventBridge → Lambda közvetlenül?

Az EventBridge tud közvetlenül Lambdát hívni, de ha a függvény hibázik vagy a terhelés megugrik, nincs igazi puffer, és a retry-lehetőségek korlátozottak. Ha közé teszünk egy SQS sort, a sor elnyeli a forgalmi csúcsokat, a Lambda a saját tempójában fogyaszt, a sikertelen üzenetek pedig újrapróbálhatók, végül DLQ-ba kerülnek. Ez a puffer az, ami a rendszert ellenállóvá teszi.

A megbízhatóság három pillére

  1. Puffer (SQS): a forgalmi csúcs nem terheli túl a fogyasztót; az üzenet megvárja a feldolgozást.
  2. Újrapróbálás (visibility timeout + redrive): a hibás üzenet nem vész el, hanem újra láthatóvá válik, és a Lambda újrapróbálja.
  3. Végállomás (DLQ): ami N próba után sem megy át, egy külön sorba kerül vizsgálatra — nem tűnik el.

Az SQS sorok DLQ-val, Terraformban

Először a dead-letter queue, majd a fő sor, ami a redrive_policy-ban a DLQ-ra hivatkozik. A maxReceiveCount mondja meg, hányszor próbálkozzon a rendszer, mielőtt az üzenetet a DLQ-ba tolja.

hcl
resource "aws_sqs_queue" "dlq" {
  name                      = "${var.project}-dlq"
  message_retention_seconds = 1209600 # 14 nap — legyen időd vizsgálni
  tags                      = local.tags
}

resource "aws_sqs_queue" "main" {
  name                       = "${var.project}-main"
  visibility_timeout_seconds = 180
  message_retention_seconds  = 345600 # 4 nap

  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dlq.arn
    maxReceiveCount     = 5
  })

  tags = local.tags
}

Az EventBridge szabály és a szűrés

Az EventBridge ereje a tartalom-alapú szűrés: a szabály event_pattern-je pontosan meghatározza, mely események érdekelnek. Így nem a fogyasztóban dobsz el üzeneteket, hanem már a routingnál — ami olcsóbb és tisztább.

hcl
resource "aws_cloudwatch_event_bus" "app" {
  name = "${var.project}-bus"
}

resource "aws_cloudwatch_event_rule" "orders" {
  name           = "${var.project}-order-created"
  event_bus_name = aws_cloudwatch_event_bus.app.name

  event_pattern = jsonencode({
    source        = ["app.orders"]
    "detail-type" = ["OrderCreated"]
    detail = {
      amount = [{ numeric = [">", 0] }]
    }
  })
}

resource "aws_cloudwatch_event_target" "to_sqs" {
  rule           = aws_cloudwatch_event_rule.orders.name
  event_bus_name = aws_cloudwatch_event_bus.app.name
  arn            = aws_sqs_queue.main.arn
}

Hogy az EventBridge írhasson a sorba, a sornak engednie kell ezt egy queue policyben:

hcl
data "aws_iam_policy_document" "allow_eventbridge" {
  statement {
    actions   = ["sqs:SendMessage"]
    resources = [aws_sqs_queue.main.arn]
    principals {
      type        = "Service"
      identifiers = ["events.amazonaws.com"]
    }
    condition {
      test     = "ArnEquals"
      variable = "aws:SourceArn"
      values   = [aws_cloudwatch_event_rule.orders.arn]
    }
  }
}

resource "aws_sqs_queue_policy" "main" {
  queue_url = aws_sqs_queue.main.id
  policy    = data.aws_iam_policy_document.allow_eventbridge.json
}

A Lambda mint fogyasztó

A Lambdát egy event source mapping köti a sorhoz. A batch_size és a maximum_batching_window_in_seconds szabályozza, mekkora kötegben és milyen gyakran fut. A részleges batch-válasz (ReportBatchItemFailures) fontos: így egyetlen hibás üzenet nem buktatja meg az egész köteget.

hcl
resource "aws_lambda_event_source_mapping" "consumer" {
  event_source_arn = aws_sqs_queue.main.arn
  function_name    = aws_lambda_function.consumer.arn
  batch_size       = 10
  maximum_batching_window_in_seconds = 5

  function_response_types = ["ReportBatchItemFailures"]
}

A DLQ nem szemetes — figyeld!

A dead-letter queue csak akkor ér valamit, ha értesülsz róla, hogy került bele üzenet. Egy CloudWatch alarm a DLQ mélységére azonnal jelez, ha valami rendszeresen elbukik — így nem hetekkel később, egy ügyfélpanaszból tudod meg.

hcl
resource "aws_cloudwatch_metric_alarm" "dlq_not_empty" {
  alarm_name          = "${var.project}-dlq-not-empty"
  namespace           = "AWS/SQS"
  metric_name         = "ApproximateNumberOfMessagesVisible"
  dimensions          = { QueueName = aws_sqs_queue.dlq.name }
  statistic           = "Maximum"
  period              = 60
  evaluation_periods  = 1
  threshold           = 0
  comparison_operator = "GreaterThanThreshold"
  alarm_actions       = [var.alerts_topic_arn]
  tags                = local.tags
}

Feldolgozási garanciák: mit ígérhetsz?

Fontos tisztán látni, milyen garanciát ad ez a felállás, hogy a fogyasztó kódját helyesen írd meg.

GaranciaEz a felállásMit jelent a kódban?
Kézbesítéslegalább egyszeridempotens feldolgozás kell
Sorrendnem garantált (standard SQS)ne feltételezz sorrendet
Elvesztésnincs (retry + DLQ)a DLQ-t figyelni kell
Duplikációelőfordulhatdedup kulcs / idempotencia

Mit jelent ez neked?

Ez a pipeline — EventBridge-szűrés, SQS-puffer, Lambda-fogyasztó részleges batch-válasszal, és figyelt DLQ — a megbízható aszinkron feldolgozás bevált mintája. A lényeg a hozzáállásban van: feltételezd, hogy minden hibázhat, és tervezz rá. Az SGS „legalább egyszer” kézbesít, ezért a feldolgozásod legyen idempotens; a sorrend nem garantált, ezért ne múljon rajta semmi; és a DLQ-t figyeld riasztással, különben csak egy csendes szemetes lesz. Ha ezt betartod, az architektúra kibírja a csúcsforgalmat és a részleges hibákat is anélkül, hogy egyetlen esemény nyomtalanul eltűnne.