Trong các hệ thống xây dựng trên Azure, ba dịch vụ messaging thường khiến developer dễ nhầm nhất là:
“`text
Azure Event Grid
Azure Service Bus
Azure Event Hubs
“`
Cả ba đều liên quan đến message/event, nhưng mục đích sử dụng rất khác nhau.
Nếu chọn sai service, hệ thống có thể vẫn chạy nhưng sẽ gặp vấn đề khi scale, retry, xử lý duplicate, đảm bảo ordering hoặc vận hành production.
Bài viết này tập trung vào **Azure Event Grid**, cách nó hoạt động, những điểm cần lưu ý trong production, và cách phân biệt với Service Bus và Event Hubs.
—
## 1. Azure Event Grid là gì?
Azure Event Grid là một **fully managed publish-subscribe event distribution service**.
Có thể hiểu đơn giản:
“`text
Something happened
↓
Event Grid
↓
Notify subscribers
“`
Ví dụ:
“`text
BlobCreated
OrderCompleted
UserRegistered
SubscriptionExpired
“`
Publisher chỉ thông báo:
> Một sự kiện đã xảy ra.
Publisher không cần biết consumer nào sẽ xử lý event đó.
Ví dụ:
“`text
Order Service
↓
OrderCompleted
↓
Event Grid
├── Notification Service
├── Analytics Service
└── Audit Service
“`
Đây là mô hình **publish/subscribe**.
—
# 2. Event không giống Command
Đây là distinction quan trọng khi thiết kế event-driven system.
## Event
Event mô tả một sự thật đã xảy ra.
Ví dụ:
“`text
OrderCompleted
UserCheckedOut
BlobCreated
RewardGranted
“`
Ý nghĩa:
“`text
Something happened.
“`
Publisher không yêu cầu cụ thể một consumer phải làm gì.
—
## Command
Command yêu cầu một hành động được thực hiện.
Ví dụ:
“`text
ProcessPayment
GrantReward
GenerateInvoice
SendNotification
“`
Ý nghĩa:
“`text
Please do this.
“`
Command thường có expectation rõ ràng rằng operation phải được xử lý.
Đây là một cách đơn giản để phân biệt Event Grid và Service Bus:
“`text
Event Grid
→ Something happened.
Service Bus
→ Please do this.
“`
—
# 3. Các thành phần chính của Event Grid
Một flow Event Grid cơ bản:
“`text
Event Source
↓
Topic
↓
Event Subscription
↓
Event Handler
“`
—
## Event Source
Event Source là nơi phát sinh event.
Ví dụ:
“`text
Azure Blob Storage
Custom .NET Application
Azure Resource Manager
Event Hubs
“`
—
## Topic
Topic là nơi publisher publish event.
Có thể hình dung:
“`text
Producer
↓
Topic
“`
Các subscriber đăng ký nhận event từ topic.
Event Grid có nhiều loại topic, trong đó phổ biến là:
“`text
System Topic
Custom Topic
Namespace Topic
“`
### System Topic
Đại diện cho events đến từ Azure service.
Ví dụ:
“`text
Azure Storage
↓
BlobCreated
↓
System Topic
“`
—
### Custom Topic
Dùng khi application tự publish business event.
Ví dụ:
“`text
Order Service
↓
OrderCompleted
↓
Custom Topic
“`
—
# 4. Event Subscription
Event Subscription định nghĩa:
“`text
Which events?
+
Where should they go?
“`
Ví dụ:
“`text
OrderEvents Topic
“`
Có:
“`text
Subscription A
OrderCompleted
→ Notification
Subscription B
OrderCompleted
→ Analytics
Subscription C
OrderCancelled
→ Customer Support
“`
Publisher không cần biết có bao nhiêu subscription tồn tại.
Đây chính là loose coupling.
—
# 5. Event Filtering
Không phải subscriber nào cũng cần nhận tất cả event.
Giả sử topic có:
“`text
OrderCreated
OrderPaid
OrderCancelled
OrderCompleted
“`
Notification service chỉ quan tâm:
“`text
OrderPaid
OrderCompleted
“`
Event Grid có thể filter ngay trước khi delivery.
Có thể filter dựa trên:
“`text
Event type
Subject
Prefix / suffix
Event attributes
Event data
“`
Ví dụ Blob Storage:
“`text
/images/a.jpg
/documents/a.pdf
/images/b.jpg
“`
Có thể chỉ subscribe:
“`text
subjectEndsWith = “.jpg”
“`
Một nguyên tắc tốt:
> Nếu broker có thể filter event thì nên filter ở broker thay vì gửi toàn bộ event xuống consumer rồi mới bỏ đi.
—
# 6. Event Schema
Khi publish event, cần có một schema rõ ràng.
Một cách phổ biến hiện nay là sử dụng **CloudEvents**.
Ví dụ:
“`json
{
“specversion”: “1.0”,
“id”: “event-123”,
“type”: “attendance.checked_out”,
“source”: “/attendance”,
“subject”: “/attendance/A123”,
“time”: “2026-09-11T03:00:00Z”,
“data”: {
“attendanceId”: “A123”,
“userId”: “U100”
}
}
“`
Có thể chia thành:
“`text
Event metadata
id
type
source
subject
time
“`
và:
“`text
Business payload
data
“`
Không nên nhét toàn bộ business information vào tên topic hoặc event name.
—
# 7. Delivery Guarantee
Một điểm cực kỳ quan trọng:
> Event Grid là **at-least-once delivery**.
Điều này có nghĩa:
“`text
Event A
→ có thể được nhận 1 lần
“`
hoặc:
“`text
Event A
→ có thể được nhận nhiều lần
“`
Không được assume rằng mỗi event chỉ xuất hiện đúng một lần.
—
# 8. Tại sao Consumer phải Idempotent?
Giả sử event:
“`text
UserCheckedOut
“`
consumer xử lý:
“`text
Reward += 100
“`
Nếu event được delivery hai lần:
“`text
Reward += 100
Reward += 100
“`
User nhận 200 điểm.
Đó là bug.
Consumer phải thiết kế idempotent.
Ví dụ:
“`text
processed_events
event_id
processed_at
“`
với:
“`text
UNIQUE(event_id)
“`
Flow:
“`text
Receive Event
↓
Already processed?
│
├── Yes
│ ↓
│ Ignore safely
│
└── No
↓
Process business logic
↓
Mark processed
“`
Một giải pháp mạnh hơn là lưu trạng thái processed cùng business operation trong một transaction.
Ví dụ:
“`text
BEGIN
INSERT processed_events(event_id)
CREATE reward
COMMIT
“`
Nếu duplicate event đến:
“`text
INSERT event_id
→ already exists
“`
consumer có thể ignore an toàn.
—
# 9. Event Grid không đảm bảo Ordering
Không nên assume:
“`text
Event 1
Event 2
Event 3
“`
sẽ luôn đến:
“`text
1
2
3
“`
Nếu business phụ thuộc strict ordering:
“`text
CheckIn
→ CheckOut
“`
thì cần cân nhắc kỹ.
Có thể cần:
“`text
sequence number
version
state validation
“`
hoặc dùng một messaging solution có ordering guarantee mạnh hơn.
Ví dụ Service Bus Sessions.
—
# 10. Retry
Nếu consumer xử lý thất bại:
“`text
Event Grid
↓
Consumer
↓
HTTP 503
“`
Event Grid có thể retry.
Concept:
“`text
Delivery
↓
Failure
↓
Retry
↓
Retry
↓
Retry
“`
Tuy nhiên cần phân biệt transient error và permanent error.
### Transient errors
Ví dụ:
“`text
Timeout
503 Service Unavailable
Temporary network issue
“`
Retry có ý nghĩa.
### Permanent errors
Ví dụ:
“`text
Invalid payload
Unauthorized
Business validation failed permanently
“`
Retry nhiều lần thường không giúp ích.
Consumer vẫn cần được thiết kế để hiểu loại lỗi nào có thể retry.
—
# 11. Dead Letter
Nếu Event Grid retry nhiều lần nhưng vẫn không delivery được:
“`text
Event Grid
↓
Retry
↓
Retry
↓
Still failed
↓
Dead Letter
“`
Dead Letter không nên bị xem như:
“`text
message failed
→ done
“`
Production flow phải là:
“`text
Dead Letter
↓
Alert
↓
Investigate
↓
Fix root cause
↓
Replay / Manual processing
“`
Một Senior Engineer cần nghĩ thêm:
“`text
Ai monitor DLQ?
Bao lâu thì alert?
Replay bằng cách nào?
Replay có gây duplicate không?
Consumer có idempotent không?
“`
—
# 12. Push và Pull Delivery
Event Grid có thể hoạt động theo nhiều cách.
## Push
Event Grid chủ động gửi event tới consumer.
“`text
Event Grid
↓ HTTP
Consumer
“`
Phù hợp với:
“`text
Azure Function
Webhook
Reactive processing
“`
—
## Pull
Consumer chủ động lấy event.
“`text
Consumer
↓
Receive events
↓
Event Grid
“`
Pull phù hợp khi consumer cần kiểm soát:
“`text
processing rate
backpressure
batching
worker capacity
“`
Nó gần với queue-consumer model hơn.
—
# 13. Event Grid vs Service Bus vs Event Hubs
Đây là phần quan trọng nhất khi làm Azure architecture.
## Event Grid
Mental model:
“`text
Something happened.
“`
Phù hợp với:
“`text
Event notification
Event routing
Fan-out
Reactive architecture
“`
Ví dụ:
“`text
BlobCreated
OrderCompleted
UserRegistered
“`
—
## Service Bus
Mental model:
“`text
Please do this.
“`
Phù hợp với:
“`text
Business commands
Work queue
Transactional messaging
Reliable processing
“`
Ví dụ:
“`text
ProcessPayment
GenerateInvoice
GrantReward
“`
Service Bus có các capability mạnh hơn cho enterprise messaging như:
“`text
Queues
Topics
Dead-letter
Sessions
Duplicate detection
Transactions
“`
—
## Event Hubs
Mental model:
“`text
Here is a continuous stream of data.
“`
Phù hợp với:
“`text
Telemetry
Logs
IoT
Clickstream
High-throughput data ingestion
“`
Ví dụ:
“`text
1M device events / second
“`
—
# 14. Một bảng so sánh nhanh
| | Event Grid | Service Bus | Event Hubs |
| ——————- | ————————- | ————————— | ——————– |
| Purpose | Event routing | Reliable messaging | Event streaming |
| Mental model | Something happened | Do this work | Stream data |
| Pub/Sub | Yes | Topics | Consumer Groups |
| Ordering | Không guarantee | Có thể dùng Sessions | Per partition |
| Transactions | Không phải trọng tâm | Có | Không phải trọng tâm |
| Duplicate detection | Consumer xử lý | Có support | Consumer xử lý |
| Replay | Không phải use case chính | Không phải streaming replay | Có |
| Typical use | BlobCreated | ProcessPayment | Telemetry |
—
# 15. Khi nào dùng Event Grid?
Event Grid phù hợp khi:
“`text
Một sự kiện đã xảy ra
“`
và nhiều hệ thống có thể quan tâm.
Ví dụ:
“`text
UserRegistered
↓
Event Grid
├── Welcome Email
├── Analytics
└── Audit
“`
Hoặc:
“`text
BlobCreated
↓
Event Grid
├── Image Processor
└── Metadata Processor
“`
—
# 16. Khi nào không nên dùng Event Grid?
Không nên ép Event Grid vào mọi messaging problem.
Ví dụ:
“`text
ProcessPayment
“`
là một critical business operation.
Bạn có thể cần:
“`text
Retry
Dead Letter
Ordering
Transaction
Duplicate Detection
Controlled consumption
“`
Trong trường hợp đó, Service Bus có thể phù hợp hơn.
Một nguyên tắc:
> Không chọn broker dựa trên việc service nào quen dùng, mà dựa trên semantics của business operation.
—
# 17. Có thể dùng Event Grid và Service Bus cùng nhau
Không cần chọn chỉ một.
Ví dụ:
“`text
Attendance Service
↓
Outbox
↓
Service Bus
↓
Reward Worker
“`
Reward processing là critical business operation.
Sau khi thành công:
“`text
RewardGranted
↓
Event Grid
├── Notification
├── Analytics
└── Audit
“`
Ở đây:
“`text
Service Bus
→ xử lý business work đáng tin cậy
Event Grid
→ broadcast business fact
“`
Hai service bổ sung cho nhau.
—
# 18. Security
Trong production Azure, tránh hard-code:
“`text
connection string
access key
secret
“`
Khi có thể, nên sử dụng:
“`text
Managed Identity
+
Azure RBAC
“`
Ví dụ:
“`text
Event Grid
↓
Managed Identity
↓
RBAC permission
↓
Service Bus
“`
Điều này giúp giảm:
“`text
secret management
secret rotation
credential leakage
“`
—
# 19. Observability cho Event Grid
Event-driven system rất khó debug nếu không có traceability.
Một event nên có các metadata hữu ích:
“`text
event_id
correlation_id
event_type
message_id
aggregate_id
“`
Ví dụ:
“`json
{
“event_id”: “EVT100”,
“correlation_id”: “TRACE001”,
“event_type”: “AttendanceCheckedOut”,
“attendance_id”: “ATT123”
}
“`
Một E2E flow:
“`text
API
↓
Event Grid
↓
Function
↓
Service Bus
↓
Worker
“`
nên có khả năng trace được toàn bộ bằng:
“`text
TraceId
CorrelationId
EventId
“`
Metrics nên theo dõi:
“`text
events_published
delivery_failures
dead_letter_count
processing_duration
consumer_failure_rate
“`
Nếu:
“`text
dead_letter_count > 0
“`
nên có alert.
—
# 20. Failure Scenario thực tế
Giả sử consumer xử lý:
“`text
OrderCompleted
“`
Flow:
“`text
Receive event
↓
Create invoice
↓
DB COMMIT
↓
Application crashes
“`
Consumer chưa trả success.
Event Grid có thể retry.
Consumer nhận lại:
“`text
OrderCompleted
“`
Nếu không idempotent:
“`text
Invoice #1
Invoice #2
“`
Sai.
Nếu có Inbox / ProcessedEvent:
“`text
BEGIN
INSERT processed_event(event_id)
CREATE invoice
COMMIT
“`
duplicate delivery:
“`text
event_id already exists
“`
=> consumer biết event đã được xử lý.
Đây là lý do:
> Retry và Idempotency luôn phải được thiết kế cùng nhau.
—
# 21. Cách trả lời khi phỏng vấn
Nếu interviewer hỏi:
> When would you use Event Grid instead of Service Bus?
Có thể trả lời:
> I use Event Grid when I need to distribute discrete business or infrastructure events to multiple independent subscribers. The publisher only communicates that something happened and does not need to know who consumes the event.
>
> For business commands that must be reliably processed, such as payment or reward processing, I normally prefer Service Bus because it provides stronger messaging capabilities for transactional workloads.
>
> I also assume Event Grid delivery is at least once and unordered, so consumers must be idempotent and should not depend on delivery order.
—
# 22. Mental Model dễ nhớ
Có thể nhớ ba Azure services bằng ba câu:
“`text
Event Grid
→ Something happened.
Service Bus
→ Please do this.
Event Hubs
→ Here is a stream of data.
“`
Và riêng Event Grid:
“`text
Publish Event
↓
Event Grid
↓
Filter
↓
Fan-out
↓
Subscribers
“`
Nhưng khi triển khai production phải bổ sung:
“`text
At-least-once
↓
Idempotency
No ordering guarantee
↓
Don’t depend on arrival order
Delivery failure
↓
Retry + Dead Letter
Production operation
↓
Metrics + Logs + Tracing
“`
—
# Kết luận
Azure Event Grid không đơn giản chỉ là một service để gửi message.
Nó phù hợp với kiến trúc:
“`text
Event-driven
Publish / Subscribe
Loose coupling
Reactive systems
“`
Điểm quan trọng nhất khi thiết kế là hiểu semantics của event.
Nếu message mang nghĩa:
“`text
Something happened
“`
Event Grid là một candidate rất tốt.
Nếu message mang nghĩa:
“`text
Please perform this critical work
“`
Service Bus thường phù hợp hơn.
Nếu dữ liệu là:
“`text
A continuous high-volume stream
“`
hãy nghĩ tới Event Hubs.
Cuối cùng, dù sử dụng service nào, production architecture vẫn cần xem xét:
“`text
Delivery guarantee
Retry
Duplicate
Idempotency
Ordering
Dead Letter
Security
Observability
Recovery
“`
Đó mới là phần quyết định một event-driven system có thực sự đáng tin cậy hay không.