L
Cloud Architecture & Serverless • Literally Web ID

Architecting Serverless Event-Driven Systems on AWS Lambda and SQS

⏱️ 22 menit baca • 📅 14 Oct 2026 • 👁️ 1 views
Architecting Serverless Event-Driven Systems on AWS Lambda and SQS

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap arsitektur sistem web modern, tantangan terbesar yang dihadapi oleh Cloud Architects dan DevOps Engineers bukanlah bagaimana membangun fitur dengan cepat, melainkan bagaimana memastikan sistem tetap tangguh (resilient), dapat diskalakan secara otomatis (auto-scalable), dan efisien dari segi biaya (cost-effective) saat menghadapi lonjakan trafik yang masif. Pendekatan monolitik tradisional yang mengandalkan server web konvensional (seperti Nginx/Apache yang memproses request secara sinkron di belakang PHP-FPM atau Node.js) sering kali menemui jalan buntu ketika dihadapkan pada lonjakan beban puncak (traffic spikes). Koneksi basis data menjadi jenuh, proses latar belakang (background jobs) memblokir thread utama, dan server mengalami exhaustion sumber daya.

Pendekatan nir-server (Serverless) melalui AWS Lambda yang dikombinasikan dengan Amazon SQS (Simple Queue Service) dan SNS (Simple Notification Service) menawarkan solusi revolusioner. Namun, merancang sistem event-driven di cloud publik memiliki kompleksitas tersendiri. Masalah umum di dunia nyata meliputi Cold Starts pada AWS Lambda yang menyebabkan latensi tak terduga, hilangnya data akibat penanganan pesan yang buruk, konfigurasi Dead Letter Queues (DLQ) yang tidak memadai, serta celah keamanan akibat kebijakan IAM (Identity and Access Management) yang terlalu longgar (over-permissioned).

Sebelum vs Sesudah Penerapan Arsitektur Event-Driven SQS & Lambda

Aspek Sebelum: Monolitik & Sinkron (Nginx + PHP-FPM + MySQL) Sesudah: Serverless Event-Driven (AWS Lambda + SQS)
Skalabilitas Terbatas oleh kapasitas vertikal atau horizontal server web; rentan terhadap connection pool exhaustion. Skalabilitas otomatis instan dari 0 hingga ribuan instans eksekusi paralel tanpa intervensi manual.
Penanganan Beban Request diblokir jika backend sibuk; gateway timeout sering terjadi pada proses berat. Request langsung diterima, dimasukkan ke dalam antrean SQS secara asinkron, dan diproses tanpa membebani pemanggil.
Model Biaya Biaya operasional tetap (24/7 instance uptime), boros saat trafik rendah. Model Pay-as-you-go murni; biaya berdasarkan milidetik eksekusi Lambda dan jumlah permintaan SQS.
Ketahanan Kegagalan Jika server mati, request yang sedang berjalan hilang kecuali ada mekanisme replikasi kompleks. Pesan aman di dalam SQS (daya tahan 99.999999999%), dapat diproses ulang atau dikirim ke DLQ jika gagal.

2. Konsep Inti & Deep Dive Arsitektur

Sistem Event-Driven Architecture (EDA) berbasis AWS Lambda dan SQS beroperasi atas prinsip pemisahan (decoupling) antara produsen (producer) dan konsumen (consumer) data. Produsen (misalnya aplikasi web frontend atau API Gateway) mengirimkan event ke antrean SQS atau topik SNS, dan segera menerima respons sukses. AWS Lambda kemudian bertindak sebagai consumer yang mengambil batch pesan dari SQS dan mengeksekusi logika bisnis secara paralel.

Alur Kerja Sistem (Under the Hood)

+------------------+         +-----------------+         +------------------+
|  API Gateway /   |  Push   |    Amazon SNS   |  Fanout |    Amazon SQS    |
|  Web Application | ------->|  (Pub/Sub Topic)| ------->|   (FIFO/Standard)|
+------------------+         +-----------------+         +------------------+
                                                                  |
                                                          Poll Batch
                                                                  v
                                                         +------------------+
                                                         |    AWS Lambda    |
                                                         |    (Consumer)    |
                                                         +------------------+
                                                                  |
                                                    +-------------+-------------+
                                                    |                           |
                                                 Success                      Failure
                                                    |                           |
                                                    v                           v
                                            Delete from SQS            Retry / DLQ Routing
  1. Inisiasi Event: Klien mengirimkan HTTP request ke API Gateway atau langsung ke infrastruktur web (Nginx/Node.js). Sinyal diubah menjadi payload JSON.
  2. Publikasi & Antrean: Payload dikirim ke Amazon SQS Standard atau FIFO Queue. SQS menyimpan pesan secara terdistribusi di beberapa zona ketersediaan (Availability Zones).
  3. Polling & Eksekusi Lambda: Layanan poll internal AWS Lambda secara otomatis membaca pesan dari SQS dalam bentuk batch (hingga 10 pesan per batch secara default).
  4. Pemrosesan & Pengakuan (Acknowledgment): Lambda menjalankan fungsi. Jika eksekusi berhasil untuk seluruh batch, Lambda mengirimkan sinyal hapus ke SQS. Jika terjadi exception, pesan dikembalikan ke antrean berdasarkan visibilitas timeout (Visibility Timeout).

3. Perbandingan Komparatif / Analisis Pendekatan

Dalam membangun sistem antrean dan komputasi nir-server di AWS, arsitek sering kali harus memilih antara beberapa pendekatan infrastruktur. Tabel di bawah membedah trade-off dari tiga pendekatan umum:

Parameter Komparasi SQS Standard + Lambda SQS FIFO + Lambda Redis Queue (di EC2/ECS) + Worker
Throughput Hampir tak terbatas (High throughput). Dibatasi hingga 300 msg/detik (atau 3000 dengan batching). Tergantung kapasitas memori dan jaringan instance Redis.
Urutan Pesan Tidak dijamin (Best-effort ordering). Dijamin ketat (First-In-First-Out). Tergantung implementasi consumer dan struktur data.
Duplikasi Pesan Kemungkinan at-least-once delivery (duplikat bisa terjadi). Mendukung Exactly-once processing (mencegah duplikasi). Tergantung konfigurasi aplikasi (idempotency diperlukan).
Operational Overhead Sangat rendah (Fully managed). Sangat rendah (Fully managed). Tinggi (Manajemen patch, scaling, failover Redis).
Latensi Pemrosesan Rendah (< 1 detik). Sedikit lebih tinggi karena batasan urutan. Sangat rendah (< 100ms jika di server lokal).

[!TIP] Gunakan SQS Standard kecuali aplikasi Anda secara mutlak memerlukan urutan eksekusi yang ketat (misalnya sistem perbankan atau log transaksi akuntansi). SQS Standard menawarkan throughput jauh lebih tinggi dan biaya yang lebih efisien.

4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum melangkah ke implementasi teknis, pastikan environment pengembangan Anda memenuhi spesifikasi berikut:

  • AWS CLI (v2.x) terinstal dan dikonfigurasi dengan kredensial IAM yang memiliki hak akses administratif yang memadai untuk pengujian.
  • Node.js (v18.x / v20.x LTS) atau Go (v1.21+) sebagai runtime utama AWS Lambda.
  • Terraform (v1.6+) atau AWS Serverless Application Model (SAM CLI) untuk manajemen Infrastructure as Code (IaC).
  • Git untuk version control.

Verifikasi instalasi AWS CLI dengan perintah terminal:

aws --version
aws sts get-caller-identity

Pastikan variabel lingkungan dasar telah diatur di mesin lokal Anda:

export AWS_REGION="us-east-1"
export AWS_ACCOUNT_ID="123456789012"

5. Panduan Implementasi Langkah demi Langkah

Berikut adalah implementasi end-to-end menggunakan Terraform untuk mendefinisikan infrastruktur (SQS, DLQ, Lambda, IAM Roles) dan kode Node.js (TypeScript/JavaScript) untuk fungsi Lambda.

A. Definisi Infrastruktur (Terraform: main.tf)

terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# 1. Dead Letter Queue (DLQ)
resource "aws_sqs_queue" "order_dlq" {
  name                      = "order-processing-dlq"
  message_retention_seconds = 1209600 # 14 hari
}

# 2. Main SQS Queue dengan Redrive Policy ke DLQ
resource "aws_sqs_queue" "order_queue" {
  name                       = "order-processing-queue"
  visibility_timeout_seconds = 60
  message_retention_seconds  = 345600 # 4 hari
  receive_wait_time_seconds  = 20     # Long polling

  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.order_dlq.arn
    maxReceiveCount     = 3
  })
}

# 3. IAM Role untuk AWS Lambda
resource "aws_iam_role" "lambda_exec_role" {
  name = "order_lambda_execution_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Principal = {
        Service = "lambda.amazonaws.com"
      }
    }]
  })
}

# 4. IAM Policy untuk Logging dan Akses SQS
resource "aws_iam_policy" "lambda_sqs_policy" {
  name = "lambda_sqs_policy"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ]
        Resource = "arn:aws:logs:*:*:*"
      },
      {
        Effect = "Allow"
        Action = [
          "sqs:ReceiveMessage",
          "sqs:DeleteMessage",
          "sqs:GetQueueAttributes"
        ]
        Resource = aws_sqs_queue.order_queue.arn
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "attach_sqs_policy" {
  role       = aws_iam_role.lambda_exec_role.name
  policy_arn = aws_iam_policy.lambda_sqs_policy.arn
}

# 5. AWS Lambda Function
resource "aws_lambda_function" "order_processor" {
  filename      = "lambda_payload.zip"
  function_name = "OrderProcessorFunction"
  role          = aws_iam_role.lambda_exec_role.arn
  handler       = "index.handler"
  runtime       = "nodejs20.x"
  memory_size   = 512
  timeout       = 30

  environment {
    variables = {
      ENV = "production"
    }
  }
}

# 6. Event Source Mapping (Hubungkan SQS ke Lambda)
resource "aws_lambda_event_source_mapping" "sqs_lambda_trigger" {
  event_source_arn                   = aws_sqs_queue.order_queue.arn
  function_name                      = aws_lambda_function.order_processor.arn
  batch_size                         = 10
  maximum_batching_window_in_seconds = 5

  scaling_config {
    maximum_concurrency = 50
  }
}

B. Kode Lambda (Node.js / JavaScript: index.js)

/**
 * AWS Lambda Function untuk Memproses Pesanan dari SQS
 * Runtime: Node.js 20.x
 */
exports.handler = async (event) => {
    console.log(`Menerima batch berisi ${event.Records.length} pesan dari SQS.`);

    const batchItemFailures = [];

    for (const record of event.Records) {
        try {
            console.log(`Memproses Message ID: ${record.messageId}`);
            const body = JSON.parse(record.body);
            
            // Validasi struktur payload
            if (!body.orderId || !body.amount) {
                throw new Error(`Payload tidak valid untuk message ID: ${record.messageId}`);
            }

            // Simulasi pemrosesan bisnis (Simulasi simpan ke database / panggil payment gateway)
            await processOrder(body);

            console.log(`Berhasil memproses pesanan: ${body.orderId}`);
        } catch (error) {
            console.error(`Gagal memproses pesan ${record.messageId}:`, error.message);
            
            // Laporkan kegagalan pesan individual agar pesan lain dalam batch tetap sukses
            batchItemFailures.push({ itemIdentifier: record.messageId });
        }
    }

    return { batchItemFailures };
};

async function processOrder(orderData) {
    // Simulasi latensi pemrosesan IO
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // Simulasi error acak untuk pengujian DLQ (5% probabilitas error)
            if (Math.random() < 0.05) {
                reject(new Error("Database timeout saat menyimpan pesanan."));
            } else {
                resolve(true);
            }
        }, 100);
    });
}

Kemas file JavaScript menjadi arsip ZIP untuk di-deploy:

zip lambda_payload.zip index.js
terraform init
terraform apply -auto-approve

6. Bedah Kode & Penjelasan Detail Baris Kunci

Mari kita bedah beberapa komponen paling krusial dari konfigurasi dan kode di atas:

  • redrive_policy pada SQS: Parameter maxReceiveCount = 3 menentukan berapa kali sebuah pesan dapat dicoba diproses oleh Lambda sebelum akhirnya dianggap gagal total dan dipindahkan secara permanen ke aws_sqs_queue.order_dlq. Hal ini mencegah infinite poison pill loop di mana pesan korup memblokir antrean selamanya.
  • batchItemFailures pada Lambda Response: Konfigurasi ini merupakan fitur tingkat lanjut dari integrasi SQS-Lambda. Secara tradisional, jika satu pesan gagal dalam batch berisi 10 pesan, seluruh batch akan dikembalikan ke SQS, menyebabkan 9 pesan sehat lainnya ikut diproses ulang (reprocessed). Dengan mengembalikan objek batchItemFailures, AWS SQS hanya akan mengembalikan pesan yang benar-benar gagal ke antrean.
  • maximum_concurrency = 50: Parameter pembatas skala konkurensi Lambda pada Event Source Mapping. Ini krusial untuk melindungi sistem hilir (seperti database relasional tradisional yang memiliki batas maksimum koneksi atau connection pool) agar tidak kebanjiran koneksi bersamaan dari ribuan instans Lambda.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Bayangkan sebuah platform e-commerce skala besar yang menghadapi Flash Sale dengan 100.000 transaksi per menit. Jika arsitektur web konvensional digunakan, server basis data akan langsung down akibat lonjakan koneksi.

Dengan arsitektur SQS + Lambda:

  1. Penerimaan Cepat: Gateway menerima 100.000 request dalam hitungan detik dan langsung memasukkannya ke SQS Standard. Pengguna mendapat respons instan "Pesanan Anda sedang diproses".
  2. Buffer Elastis: SQS bertindak sebagai penyangga (buffer) yang menyerap lonjakan trafik instan tersebut tanpa merusak sistem di belakangnya.
  3. Konsumsi Terkendali: AWS Lambda membaca pesan dari SQS secara bertahap sesuai batas konkurensi yang diatur (misalnya 500 eksekusi paralel), menjaga beban pada database tetap stabil dan dapat diprediksi.

8. Common Pitfalls, Anti-Patterns & Solusi

  1. Mengabaikan Idempotensi (Idempotency):
    • Anti-Pattern: Mengasumsikan pesan SQS hanya dikirim sekali. Karena sifat at-least-once delivery SQS, duplikat pesan dapat terjadi pada kondisi jaringan tertentu.
    • Solusi: Simpan status transaksi atau messageId di Redis/DynamoDB dengan batasan waktu unik (TTL) untuk memastikan operasi bisnis hanya dieksekusi sekali.
  2. Visibility Timeout yang Terlalu Singkat:
    • Anti-Pattern: Mengatur visibility_timeout_seconds lebih kecil dari waktu eksekusi fungsi Lambda.
    • Solusi: Pastikan Visibility Timeout SQS minimal 6 kali durasi timeout maksimum fungsi Lambda ditambah waktu pemrosesan rata-rata.
  3. Kurangnya Monitoring pada DLQ:
    • Anti-Pattern: Mengirim pesan ke Dead Letter Queue tetapi tidak memasang CloudWatch Alarm.
    • Solusi: Buat alarm CloudWatch yang memantau metrik ApproximateNumberOfMessagesVisible pada DLQ dan kirimkan notifikasi ke SNS/Slack jika nilainya > 0.

9. Benchmark, Metrik Kinerja & Tuning Performa

Untuk menguji ketahanan sistem yang telah dibangun, Anda dapat menggunakan tool beban seperti k6 atau Apache Bench (ab) untuk menghujani endpoint API yang memicu SQS.

Contoh Skrip Pengujian k6 (load-test.js):

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
    stages: [
        { duration: '30s', target: 500 },  // Ramp up hingga 500 pengguna
        { duration: '1m', target: 2000 },  // Lonjakan tinggi ke 2000 pengguna
        { duration: '30s', target: 0 },    // Ramp down
    ],
};

export default function () {
    const url = 'https://api.example.com/v1/orders';
    const payload = JSON.stringify({
        orderId: `ord_${Math.random().toString(36.substring(2))}`,
        amount: Math.floor(Math.random() * 500) + 10,
        timestamp: Date.now(),
    });

    const params = {
        headers: { 'Content-Type': 'application/json' },
    };

    let res = http.post(url, payload, params);
    check(res, {
        'status adalah 202 Accepted': (r) => r.status === 202,
    });
    sleep(0.1);
}

Metrik Kunci yang Harus Dipantau di CloudWatch:

  • ApproximateAgeOfMessages: Menunjukkan seberapa lama pesan tertahan di antrean sebelum diproses. Jika metrik ini meningkat, berarti kecepatan konsumsi Lambda kalah cepat dibanding laju produsen pesan.
  • IteratorAge: Metrik khusus untuk SQS-Lambda event source mapping yang mengukur latensi pembacaan batch.
  • Errors & Throttles pada Lambda: Memastikan tidak ada pembatasan konkurensi yang tidak disengaja atau kegagalan kode massal.

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum meluncurkan arsitektur Serverless Event-Driven ini ke lingkungan produksi (production), pastikan Anda telah mencentang seluruh item dalam checklist berikut:

  • Keamanan IAM: Apakah prinsip hak istimewa terkecil (Least Privilege) telah diterapkan pada IAM Role Lambda (tidak ada penggunaan Resource: "*" yang tidak perlu)?
  • Dead Letter Queue: Apakah DLQ aktif dan terkonfigurasi dengan maxReceiveCount yang wajar (antara 3 hingga 5)?
  • Monitoring & Alarms: Apakah CloudWatch Alarms telah disiapkan untuk memantau pesan yang masuk ke DLQ?
  • Idempotensi: Apakah kode konsumer Lambda dirancang secara idempoten untuk menangani potensi duplikat pesan?
  • Konkurensi: Apakah maximum_concurrency telah diatur untuk melindungi infrastruktur hilir (database/API eksternal)?
  • Timeouts: Apakah visibilitas antrean SQS selaras dengan timeout fungsi Lambda?

Dengan merancang sistem menggunakan prinsip-prinsip di atas, Anda tidak hanya membangun sebuah aplikasi yang mampu bertahan di bawah tekanan trafik ekstrem, tetapi juga fondasi infrastruktur cloud yang efisien, mudah dikelola, dan sangat tangguh.

🏷️ Tags: #Aws #Serverless #Sqs #Lambda #Cloud Architecture