Event-Driven Architecture trên AWS: EventBridge, SNS và SQS
Danh Mục Bài Viết
- 1. Event-Driven Architecture là gì và tại sao AWS cần cả ba service?
- 2. Ranh giới thực tế giữa ba service
- 3. Xây hệ thống Order Processing thực tế — từng bước
- 4. EventBridge Content Filtering — vũ khí bị underrated
- 5. Những lỗi hay gặp và cách tránh
- 6. Câu hỏi thường gặp
- 7. Điều quan trọng hơn cả việc chọn đúng tool
- 8. Viết Lambda Consumer chuẩn cho SQS
- 9. Monitoring và Observability cho Event-Driven System
Mình nhớ rõ cái đêm hệ thống order của team tắt điện lúc 2 giờ sáng — không phải vì server quá tải, mà vì một service gọi sang service khác theo kiểu đồng bộ, service kia timeout 30 giây, và toàn bộ transaction queue bị treo. Người dùng thấy màn hình loading mãi không xong, team ops nhận alert giữa đêm, rồi sau đó là buổi họp post-mortem 3 tiếng sáng hôm sau. Ba tháng sau khi refactor sang Event-Driven Architecture, số incident giảm gần 70%. Đây là những gì mình rút ra được.
Event-Driven Architecture là gì và tại sao AWS cần cả ba service?
EDA là kiến trúc trong đó các component giao tiếp qua sự kiện thay vì gọi trực tiếp. Producer phát event khi có gì đó xảy ra, consumer lắng nghe và xử lý khi sẵn sàng — không cần biết nhau là ai, không cần online cùng lúc, không cần đợi nhau trả lời.
Sự khác biệt trong thực tế rất lớn. Với synchronous call, nếu service B chậm hoặc chết, service A bị block theo. Với EDA, service A publish event xong là free, service B xử lý lúc nó sẵn sàng. Kết quả là hệ thống resilient hơn, scale dễ hơn, và các team deploy độc lập hơn.
AWS có ba service chính cho EDA, mỗi cái phục vụ một tầng khác nhau:
- Amazon EventBridge: Serverless event bus. Nhận event từ nhiều nguồn — AWS services, SaaS, custom app — rồi filter và route đến đích phù hợp theo rule. Đây là "bộ não" phân luồng của toàn bộ kiến trúc.
- Amazon SNS (Simple Notification Service): Pub/Sub messaging theo kiểu push. Một message publish lên topic, tất cả subscriber nhận ngay. Lý tưởng cho fan-out khi một event cần trigger nhiều hành động song song.
- Amazon SQS (Simple Queue Service): Message queue bền vững theo kiểu pull. Consumer poll queue để lấy message, đảm bảo at-least-once delivery với khả năng retry và dead-letter queue.
Nghĩ mà xem: EventBridge là router thông minh, SNS là loa phát thanh, SQS là hòm thư bền vững. Ba cái kết hợp tạo ra hệ thống messaging cực kỳ linh hoạt.
Ranh giới thực tế giữa ba service
Dùng EventBridge khi: Bạn cần route event dựa trên nội dung. Ví dụ: order trên 5 triệu đi hướng VIP processing, order thông thường đi hướng khác. EventBridge hỗ trợ content filtering cực mạnh. Ngoài ra, tích hợp third-party SaaS (Shopify, Zendesk, GitHub) hoặc AWS service, EventBridge là lựa chọn tự nhiên.
Dùng SNS khi: Một event cần đến nhiều consumer cùng lúc, mỗi consumer nhận bản copy riêng. Điển hình: user đăng ký xong cần đồng thời gửi email welcome, cập nhật CRM, trigger onboarding flow, thêm vào mailing list — publish một message, bốn subscriber xử lý song song.
Dùng SQS khi: Cần đảm bảo message được xử lý, cần retry, buffer traffic spike, hoặc kiểm soát concurrency. SQS là lớp bảo vệ giúp hệ thống không bị overwhelmed khi traffic tăng đột ngột.
Tình huống Service
────────────────────────────────────────────────
Route event theo content (amount, type) EventBridge
Fan-out: 1 event → N consumer SNS
Queue + retry + dead-letter SQS
Tích hợp SaaS / AWS services EventBridge
Notification đa kênh (email, SMS) SNS
Worker pool xử lý batch SQS
Cron job / scheduled task EventBridge Scheduler
Đảm bảo thứ tự xử lý SQS FIFOPattern phổ biến nhất trong production: EventBridge → SNS → SQS. EventBridge nhận event từ nhiều nguồn, route đến SNS topic, SNS fan-out cho các SQS queue, mỗi queue có Lambda worker riêng. Pattern này scale từng phần độc lập mà không ảnh hưởng phần còn lại.
Xây hệ thống Order Processing thực tế — từng bước
Ví dụ cụ thể: khi customer đặt hàng, cần song song: gửi email xác nhận, trừ kho, tạo shipment, cập nhật analytics, notify seller. Đây là cách setup:
Bước 1: Custom Event Bus và Rule
# Tạo Custom Event Bus cho domain orders
aws events create-event-bus --name order-bus --region ap-northeast-1
# Rule route event order.created
aws events put-rule \\
--name "route-order-created" \\
--event-bus-name order-bus \\
--event-pattern '{"source":["com.myapp.orders"],"detail-type":["order.created"]}' \\
--state ENABLEDBước 2: SNS Topic và SQS Queues với DLQ
# SNS Topic
aws sns create-topic --name order-notifications
# DLQ trước
aws sqs create-queue --queue-name order-email-dlq
aws sqs create-queue --queue-name order-inventory-dlq
# Main queues với DLQ config và retention 4 ngày
aws sqs create-queue --queue-name order-email-queue \\
--attributes '{"RedrivePolicy":"{\\"deadLetterTargetArn\\":\\"arn:aws:sqs:...:order-email-dlq\\",\\"maxReceiveCount\\":\\"3\\"}","MessageRetentionPeriod":"345600","VisibilityTimeout":"60"}'
# Subscribe queues vào SNS
aws sns subscribe \\
--topic-arn arn:aws:sns:ap-northeast-1:123:order-notifications \\
--protocol sqs \\
--notification-endpoint arn:aws:sqs:ap-northeast-1:123:order-email-queueBước 3: Connect EventBridge → SNS và publish event từ code
# Set target: EventBridge rule → SNS topic
aws events put-targets \\
--rule "route-order-created" \\
--event-bus-name order-bus \\
--targets '[{"Id":"sns-target","Arn":"arn:aws:sns:ap-northeast-1:123:order-notifications"}]'import boto3, json
events_client = boto3.client("events", region_name="ap-northeast-1")
def on_order_created(order: dict):
response = events_client.put_events(
Entries=[{
"Source": "com.myapp.orders",
"DetailType": "order.created",
"Detail": json.dumps({
"orderId": order["id"],
"customerId": order["customer_id"],
"totalAmount": order["total"],
"currency": "VND",
"items": order["items"],
"createdAt": order["created_at"]
}),
"EventBusName": "order-bus"
}]
)
if response["FailedEntryCount"] > 0:
raise Exception(f"EventBridge publish failed: {response['Entries']}")
return responseMẹo xương máu: Luôn set
MessageRetentionPeriodcho SQS ít nhất 4 ngày và PHẢI có DLQ. Mình từng để default rồi gặp Lambda bị lỗi cuối tuần, message retry hết 3 lần rồi biến mất trước khi on-call kịp phản ứng. DLQ là mạng lưới an toàn không thể thiếu.
EventBridge Content Filtering — vũ khí bị underrated
EventBridge cho phép filter event theo giá trị trong detail, không chỉ theo source hay detail-type. Điều này loại bỏ hoàn toàn việc xử lý filter logic trong Lambda:
/* Route VIP orders (amount > 5 triệu) */
{
"source": ["com.myapp.orders"],
"detail-type": ["order.created"],
"detail": {
"totalAmount": [{ "numeric": [">", 5000000] }]
}
}
/* Route theo channel */
{
"detail": {
"channel": ["mobile", "app"]
}
}
/* Kết hợp nhiều condition */
{
"detail": {
"totalAmount": [{ "numeric": [">", 1000000] }],
"currency": ["VND"],
"items": { "category": ["electronics"] }
}
}EventBridge hỗ trợ: exact match, prefix, suffix, anything-but, numeric range, IP address CIDR, và exists check. Kết hợp chúng lại bạn có rule cực kỳ specific mà không tốn compute cost.
Những lỗi hay gặp và cách tránh
1. Quên SQS Queue Policy cho SNS — Mặc định SQS không cho SNS publish vào. Phải thêm resource policy vào queue:
{
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "sns.amazonaws.com"},
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:ap-northeast-1:123:your-queue",
"Condition": {
"ArnLike": {"aws:SourceArn": "arn:aws:sns:ap-northeast-1:123:your-topic"}
}
}]
}2. Không handle duplicate messages — SQS Standard đảm bảo at-least-once delivery, nghĩa là message có thể đến nhiều lần. Worker phải idempotent. Dùng DynamoDB track processed message ID với TTL, hoặc chuyển sang SQS FIFO nếu cần exactly-once.
3. Event size vượt 256KB — EventBridge giới hạn 256KB per event. Nếu payload lớn hơn, lưu vào S3 và chỉ put S3 object key vào event. Consumer tự fetch.
4. Lambda timeout lớn hơn SQS VisibilityTimeout — Nếu Lambda chưa xử lý xong mà VisibilityTimeout hết, message quay lại queue và bị xử lý lần hai. Rule: VisibilityTimeout phải lớn hơn Lambda timeout ít nhất 6 lần.
5. Dùng SNS Email subscription cho transactional email — SNS email subscription không có delivery tracking, không retry khi bounce, format không customize được. Dùng SNS → SQS → Lambda → SES thay thế.
Câu hỏi thường gặp
Q: EventBridge Scheduler vs CloudWatch Events vs EventBridge Rules?A: CloudWatch Events và EventBridge Rules là cùng một thứ (AWS merge lại). EventBridge Scheduler ra mắt năm 2022, hỗ trợ one-time schedule, timezone, flexible time window, trigger trực tiếp 270+ AWS service. Với scheduled task mới, dùng Scheduler. Với event routing theo pattern, dùng Rules.
Q: Làm sao test EDA locally?A: Dùng LocalStack — tool giả lập AWS services trên máy local. Support EventBridge, SNS, SQS khá tốt. Trong CI pipeline, dùng Testcontainers với LocalStack Docker image. EventBridge Pipes cũng có thể test qua console mà không cần code.
Q: Chi phí thực tế của bộ ba này?A: SNS $0.50/1M request, SQS $0.40/1M request, EventBridge $1.00/1M event với custom bus (AWS service event free). Với ~10M event/tháng, tổng khoảng $15–20/tháng. So với self-managed Kafka hay RabbitMQ trên EC2 (instance + ops cost), rẻ hơn đáng kể ở quy mô mid-size.
Q: Monitor và debug event flow end-to-end như thế nào?A: Bật CloudWatch Logs cho EventBridge rule, thêm EventBridge Archive để replay event khi debug. Với SQS, theo dõi
ApproximateNumberOfMessagesNotVisibleđể phát hiện backlog. Dùng AWS X-Ray trace request qua các service. Quan trọng nhất: thiết kế event schema cócorrelationIdtừ đầu để trace một business transaction qua toàn bộ flow.
Điều quan trọng hơn cả việc chọn đúng tool
EDA không phải silver bullet. Nó phù hợp khi có nhiều consumer cần react với cùng event, khi muốn decouple để deploy độc lập, hoặc cần buffer traffic spike mà không drop request. Nếu hệ thống đơn giản và team nhỏ, synchronous request-response đôi khi vẫn dễ maintain hơn.
Mình thấy nhiều team migrate sang EDA vội vàng rồi đau đầu vì distributed tracing phức tạp, eventual consistency khó debug, và developer mới khó hiểu flow. Bắt đầu nhỏ: thêm SQS cho một async job trước, rồi mới scale lên. Đừng design toàn bộ kiến trúc EDA trước khi đã có pain point thực tế.
Với EventBridge + SNS + SQS, AWS đã bỏ đi phần lớn việc ops phức tạp của distributed messaging. Thứ còn lại là bạn phải hiểu rõ trade-off của từng service để chọn đúng — và xây hệ thống đủ đơn giản để team maintain được lâu dài.
Viết Lambda Consumer chuẩn cho SQS
Lambda trigger từ SQS có đặc điểm quan trọng cần lưu ý. Khi Lambda nhận batch message mà xử lý một message lỗi không đúng cách, toàn bộ batch bị retry — kể cả những message đã xử lý thành công. Từ năm 2021, AWS hỗ trợ Partial Batch Response để chỉ retry đúng message lỗi:
import json
import boto3
from typing import Any
def handler(event: dict, context: Any) -> dict:
batch_item_failures = []
for record in event["Records"]:
try:
body = json.loads(record["body"])
# Unwrap SNS envelope nếu message đến từ SNS
if "Message" in body:
body = json.loads(body["Message"])
process_order(body)
except Exception as e:
print(f"Error processing {record['messageId']}: {e}")
batch_item_failures.append({"itemIdentifier": record["messageId"]})
# Chỉ retry những message trong danh sách này
return {"batchItemFailures": batch_item_failures}
def process_order(order: dict):
order_id = order.get("orderId")
if not order_id:
raise ValueError("Missing orderId")
# business logic...
print(f"Processed order {order_id}")Để enable Partial Batch Response trong Event Source Mapping:
aws lambda update-event-source-mapping --uuid YOUR_MAPPING_UUID --function-response-types ReportBatchItemFailuresTính năng này giúp tránh tình trạng một message "độc hại" (poison pill) làm block cả batch và khiến những message khỏe mạnh bị retry vô tận cho đến khi vào DLQ.
Monitoring và Observability cho Event-Driven System
Một trong những thách thức lớn của EDA là visibility — khi có lỗi, bạn cần biết event đang mắc kẹt ở đâu. Đây là những metric và setup mình coi là bắt buộc:
CloudWatch Alarms không thể thiếu:
SQS.ApproximateNumberOfMessagesNotVisiblecao và không giảm: Lambda đang bị throttle hoặc timeout liên tục.SQS.ApproximateAgeOfOldestMessagevượt 5 phút: message nằm queue quá lâu chưa được pick up — Lambda trigger có thể bị disabled.SNS.NumberOfNotificationsFailedlớn hơn 0: SNS không deliver được đến subscriber, thường do SQS policy sai hoặc subscriber bị xóa.- DLQ message count lớn hơn 0: Message thất bại sau đủ retry. Cần alert ngay lập tức và có playbook xử lý.
EventBridge Archive và Replay để debug:
# Tạo archive lưu toàn bộ event (khuyến nghị staging và production)
aws events create-archive --archive-name order-bus-archive --event-source-arn arn:aws:events:ap-northeast-1:123:event-bus/order-bus --retention-days 7
# Replay event để reprocess sau khi fix bug
aws events start-replay --replay-name debug-replay --event-source-arn arn:aws:events:ap-northeast-1:123:archive/order-bus-archive --event-start-time "2026-06-30T00:00:00Z" --event-end-time "2026-06-30T01:00:00Z" --destination '{"Arn":"arn:aws:events:ap-northeast-1:123:event-bus/order-bus"}'EventBridge Replay là tính năng cực kỳ hữu ích khi cần reproduce bug hoặc reprocess event sau khi fix lỗi. Mình đã dùng nó để reprocess 50.000 order sau một lần deploy lỗi — không cần viết script migration.
Correlation ID xuyên suốt toàn flow:
# Event schema nên có correlationId để trace end-to-end
{
"source": "com.myapp.orders",
"detail-type": "order.created",
"detail": {
"correlationId": "req-abc-123-xyz",
"orderId": "ORD-2026-001",
"customerId": "USR-999",
"totalAmount": 1500000,
"createdAt": "2026-06-30T08:00:00Z"
}
}Với correlationId xuyên suốt, dùng CloudWatch Logs Insights để query log từ một request qua toàn bộ service:
fields @timestamp, @message
| filter @message like "req-abc-123-xyz"
| sort @timestamp ascSetup một lần, tiết kiệm hàng giờ debug sau này.
