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à:
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:
Something happened
↓
Event Grid
↓
Notify subscribers
Ví dụ:
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ụ:
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ụ:
OrderCompleted
UserCheckedOut
BlobCreated
RewardGranted
Ý nghĩa:
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ụ:
ProcessPayment
GrantReward
GenerateInvoice
SendNotification
Ý nghĩa:
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:
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:
Event Source
↓
Topic
↓
Event Subscription
↓
Event Handler
Event Source
Event Source là nơi phát sinh event.
Ví dụ:
Azure Blob Storage
Custom .NET Application
Azure Resource Manager
Event Hubs
Topic
Topic là nơi publisher publish event.
Có thể hình dung:
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à:
System Topic
Custom Topic
Namespace Topic
System Topic
Đại diện cho events đến từ Azure service.
Ví dụ:
Azure Storage
↓
BlobCreated
↓
System Topic
Custom Topic
Dùng khi application tự publish business event.
Ví dụ:
Order Service
↓
OrderCompleted
↓
Custom Topic
4. Event Subscription
Event Subscription định nghĩa:
Which events?
+
Where should they go?
Ví dụ:
OrderEvents Topic
Có:
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ó:
OrderCreated
OrderPaid
OrderCancelled
OrderCompleted
Notification service chỉ quan tâm:
OrderPaid
OrderCompleted
Event Grid có thể filter ngay trước khi delivery.
Có thể filter dựa trên:
Event type
Subject
Prefix / suffix
Event attributes
Event data
Ví dụ Blob Storage:
/images/a.jpg
/documents/a.pdf
/images/b.jpg
Có thể chỉ subscribe:
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ụ:
{
"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:
Event metadata
id
type
source
subject
time
và:
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:
Event A
→ có thể được nhận 1 lần
hoặc:
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:
UserCheckedOut
consumer xử lý:
Reward += 100
Nếu event được delivery hai lần:
Reward += 100
Reward += 100
User nhận 200 điểm.
Đó là bug.
Consumer phải thiết kế idempotent.
Ví dụ:
processed_events
event_id
processed_at
với:
UNIQUE(event_id)
Flow:
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ụ:
BEGIN
INSERT processed_events(event_id)
CREATE reward
COMMIT
Nếu duplicate event đến:
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:
Event 1
Event 2
Event 3
sẽ luôn đến:
1
2
3
Nếu business phụ thuộc strict ordering:
CheckIn
→ CheckOut
thì cần cân nhắc kỹ.
Có thể cần:
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:
Event Grid
↓
Consumer
↓
HTTP 503
Event Grid có thể retry.
Concept:
Delivery
↓
Failure
↓
Retry
↓
Retry
↓
Retry
Tuy nhiên cần phân biệt transient error và permanent error.
Transient errors
Ví dụ:
Timeout
503 Service Unavailable
Temporary network issue
Retry có ý nghĩa.
Permanent errors
Ví dụ:
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:
Event Grid
↓
Retry
↓
Retry
↓
Still failed
↓
Dead Letter
Dead Letter không nên bị xem như:
message failed
→ done
Production flow phải là:
Dead Letter
↓
Alert
↓
Investigate
↓
Fix root cause
↓
Replay / Manual processing
Một Senior Engineer cần nghĩ thêm:
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.
Event Grid
↓ HTTP
Consumer
Phù hợp với:
Azure Function
Webhook
Reactive processing
Pull
Consumer chủ động lấy event.
Consumer
↓
Receive events
↓
Event Grid
Pull phù hợp khi consumer cần kiểm soát:
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:
Something happened.
Phù hợp với:
Event notification
Event routing
Fan-out
Reactive architecture
Ví dụ:
BlobCreated
OrderCompleted
UserRegistered
Service Bus
Mental model:
Please do this.
Phù hợp với:
Business commands
Work queue
Transactional messaging
Reliable processing
Ví dụ:
ProcessPayment
GenerateInvoice
GrantReward
Service Bus có các capability mạnh hơn cho enterprise messaging như:
Queues
Topics
Dead-letter
Sessions
Duplicate detection
Transactions
Event Hubs
Mental model:
Here is a continuous stream of data.
Phù hợp với:
Telemetry
Logs
IoT
Clickstream
High-throughput data ingestion
Ví dụ:
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:
Một sự kiện đã xảy ra
và nhiều hệ thống có thể quan tâm.
Ví dụ:
UserRegistered
↓
Event Grid
├── Welcome Email
├── Analytics
└── Audit
Hoặc:
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ụ:
ProcessPayment
là một critical business operation.
Bạn có thể cần:
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ụ:
Attendance Service
↓
Outbox
↓
Service Bus
↓
Reward Worker
Reward processing là critical business operation.
Sau khi thành công:
RewardGranted
↓
Event Grid
├── Notification
├── Analytics
└── Audit
Ở đây:
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:
connection string
access key
secret
Khi có thể, nên sử dụng:
Managed Identity
+
Azure RBAC
Ví dụ:
Event Grid
↓
Managed Identity
↓
RBAC permission
↓
Service Bus
Điều này giúp giảm:
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:
event_id
correlation_id
event_type
message_id
aggregate_id
Ví dụ:
{
"event_id": "EVT100",
"correlation_id": "TRACE001",
"event_type": "AttendanceCheckedOut",
"attendance_id": "ATT123"
}
Một E2E flow:
API
↓
Event Grid
↓
Function
↓
Service Bus
↓
Worker
nên có khả năng trace được toàn bộ bằng:
TraceId
CorrelationId
EventId
Metrics nên theo dõi:
events_published
delivery_failures
dead_letter_count
processing_duration
consumer_failure_rate
Nếu:
dead_letter_count > 0
nên có alert.
20. Failure Scenario thực tế
Giả sử consumer xử lý:
OrderCompleted
Flow:
Receive event
↓
Create invoice
↓
DB COMMIT
↓
Application crashes
Consumer chưa trả success.
Event Grid có thể retry.
Consumer nhận lại:
OrderCompleted
Nếu không idempotent:
Invoice #1
Invoice #2
Sai.
Nếu có Inbox / ProcessedEvent:
BEGIN
INSERT processed_event(event_id)
CREATE invoice
COMMIT
duplicate delivery:
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:
Event Grid
→ Something happened.
Service Bus
→ Please do this.
Event Hubs
→ Here is a stream of data.
Và riêng Event Grid:
Publish Event
↓
Event Grid
↓
Filter
↓
Fan-out
↓
Subscribers
Nhưng khi triển khai production phải bổ sung:
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:
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:
Something happened
Event Grid là một candidate rất tốt.
Nếu message mang nghĩa:
Please perform this critical work
Service Bus thường phù hợp hơn.
Nếu dữ liệu là:
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:
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.