Azure Event Hubs vs Service Bus vs Event Grid
When to Use Which — Complete Decision Framework
Introduction
Azure provides three distinct event and messaging services, each designed for different use cases. Understanding when to use each service is crucial for building efficient, scalable, and cost-effective cloud architectures.
This comprehensive guide covers:
- Service definitions — What each service does
- Detailed comparison — Features, capabilities, and limitations
- Decision frameworks — How to choose the right service
- Real-world patterns — Hybrid architectures combining multiple services
- Migration strategies — Moving between services
Service Overview
Event Hubs
Azure Event Hubs is a fully managed, real-time event streaming platform designed for high-throughput data ingestion scenarios.
┌──────────────────────────────────────────────────────────────────┐
│ EVENT HUBS │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Part 0 │ │ Part 1 │ │ Part 2 │ │ Part 3 │ │
│ │ ┌─────┐ │ │ ┌─────┐ │ │ ┌─────┐ │ │ ┌─────┐ │ │
│ │ │Msg 1│ │ │ │Msg 2│ │ │ │Msg 3│ │ │ │Msg 4│ │ │
│ │ │Msg 5│ │ │ │Msg 6│ │ │ │Msg 7│ │ │ │Msg 8│ │ │
│ │ └─────┘ │ │ └─────┘ │ │ └─────┘ │ │ └─────┘ │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │
│ └──────────────┴──────────────┴──────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Consumer Groups │ │
│ │ • Analytics │ │
│ │ • Archive │ │
│ │ • Processing │ │
│ └─────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
Key characteristics:
- Throughput-oriented — Millions of events per second
- Partition-based — Ordered within partitions
- Kafka-compatible — Native Kafka protocol support
- Capture — Built-in storage to ADLS Gen2
Service Bus
Azure Service Bus is an enterprise-grade message broker designed for reliable message queuing and pub-sub patterns with advanced features.
┌─────────────────────────────────────────────────────────────────────┐
│ SERVICE BUS │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ Queues │ │
│ ┌──────┐ ───────▶ │ ┌─────────┐│ │
│ │Sender│ │ │ Message ││──────▶┌─────────┐ │
│ └──────┘ │ └─────────┘│ │ Receiver│ │
│ │ ┌─────────┐│ └─────────┘ │
│ │ │ Message ││ │
│ │ └─────────┘│ │
│ └─────────────┘ │
│ │ │
│ ┌─────────────┐ │
│ │ Topics │ │
│ ┌──────┐ ───────▶ │ ┌─────────┐│──────▶ Subscription 1 │
│ │Sender│ │ │ Message ││ (Filter: Region=US). │
│ └──────┘ │ └─────────┘│ │
│ │ │──────▶ Subscription 2 │
│ │ │ (Filter: Type=Order) │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Key characteristics:
- Reliability-first — 99.99% uptime SLA
- Advanced features — Sessions, transactions, dead-lettering
- Message guarantee — At-least-once delivery
- Request-reply — Built-in correlation patterns
Event Grid
Azure Event Grid is an event routing service that enables event-driven, serverless architectures with near-real-time delivery.
┌──────────────────────────────────────────────────────────────────┐
│ EVENT GRID │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Sources │ │ Topics │ │ Handlers │ │
│ │ │ │ │ │ │ │
│ │ • Storage │─────▶│ • Custom │─────▶│ • Functions │ │
│ │ • IoT Hub │ │ • System │ │ • Logic Apps │ │
│ │ • Cosmos DB │ │ • Domain │ │ • Webhooks │ │
│ │ • Container │ │ │ │ • Hybrid │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Events published ──▶ Filtered ──▶ Routed to handlers │
│ (near real-time) (by subject/type) (multiple endpoints) │
│ │
└──────────────────────────────────────────────────────────────────┘
Key characteristics:
- Event-driven — Push-based notification
- Low latency — Near real-time delivery
- Fan-out — Multiple subscribers per event
- Filterable — Subject and type-based routing
Detailed Feature Comparison
Message Patterns Comparison
| Feature | Event Hubs | Service Bus | Event Grid |
|---|---|---|---|
| Core Pattern | Event streaming | Message queuing | Event routing |
| Pub-Sub | Via consumer groups | Via topics | Native |
| Queue | Not native | Full support | Not supported |
| Request-Reply | Not supported | Full support | Not supported |
| Message Sessions | Not supported | Supported | Not supported |
| Transactions | Not supported | Multi-message | Not supported |
| Dead Letter Queue | Via separate entity | Native support | Not supported |
Delivery and Ordering
| Feature | Event Hubs | Service Bus | Event Grid |
|---|---|---|---|
| Delivery Guarantee | At-least-once | At-least-once | At-least-once |
| Exactly-once | Not native | Via duplicate detection | Not supported |
| Ordering | Per-partition | Per-session or per-queue | None |
| Visibility Timeout | N/A | Configurable | N/A |
| Max Retention | 7 days | 7 days | 24 hours |
| Message Size | 1MB (256KB body) | 256KB | 1MB |
Protocol and SDK
| Feature | Event Hubs | Service Bus | Event Grid |
|---|---|---|---|
| AMQP | Yes | Yes | Via relay |
| HTTP/REST | Yes | Yes | Yes (events) |
| Kafka | Native | Not supported | Not supported |
| .NET SDK | Azure.Messaging.EventHubs | Azure.Messaging.ServiceBus | Azure.Messaging.EventGrid |
| Java SDK | azure-messaging-eventhubs | azure-messaging-servicebus | azure-messaging-eventgrid |
| Python SDK | azure-eventhub | azure-servicebus | azure-eventgrid |
Scalability Comparison
| Feature | Event Hubs | Service Bus | Event Grid |
|---|---|---|---|
| Throughput Units | 1-20 (Basic/Premium) | N/A | N/A |
| Processing Units | 1-40 (Premium) | N/A | N/A |
| Namespace Tiers | Standard, Premium, Dedicated | Standard, Premium | N/A |
| Auto-scale | Yes (Premium) | Yes (Premium) | Automatic |
| Max Partition Count | 32 (Standard), 1024 (Dedicated) | N/A | N/A |
| Concurrent Readers | Unlimited | Unlimited | Per subscription |
Decision Framework
When to Choose Event Hubs
Use Event Hubs for:
-
High-volume telemetry ingestion
- IoT device data streams
- Application logs and metrics
- Clickstream analytics
- Financial transaction streams
-
Real-time stream processing
- Stream analytics jobs
- Spark/Databricks consumption
- Real-time dashboards
-
Kafka workloads
- Existing Kafka applications
- Kafka ecosystem tools
- Event sourcing with Kafka patterns
-
Event capture for analytics
- Long-term event storage
- Batch analytics
- Data warehousing
Example: IoT Telemetry Pipeline
// Event Hub producer for IoT devices
public class IoTDevicePublisher
{
private readonly EventHubProducerClient _producer;
public async Task SendTelemetryAsync(DeviceTelemetry telemetry)
{
var eventData = new EventData(
Encoding.UTF8.GetBytes(JsonSerializer.Serialize(telemetry))
)
{
PartitionKey = telemetry.DeviceId,
ContentType = "application/json"
};
eventData.Properties["EventType"] = "Telemetry";
eventData.Properties["DeviceType"] = telemetry.DeviceType;
await _producer.SendAsync(new[] { eventData });
}
}
// Event Hub consumer for analytics
public class TelemetryProcessor
{
public async Task ProcessEventsAsync(
PartitionContext context,
IEnumerable<EventData> events)
{
foreach (var evt in events)
{
var telemetry = JsonSerializer.Deserialize<DeviceTelemetry>(
Encoding.UTF8.GetString(evt.Body));
await UpdateRealTimeDashboardAsync(telemetry);
await StoreForAnalyticsAsync(telemetry);
}
await context.CheckpointAsync();
}
}
When to Choose Service Bus
Use Service Bus for:
-
Reliable queue-based processing
- Order processing systems
- Background job processing
- Task distribution
-
Request-reply patterns
- HTTP to async conversion
- RPC over messaging
- Callback patterns
-
Message ordering requirements
- Session-based ordering
- FIFO processing
- Correlation IDs
-
Complex workflows
- Multi-message transactions
- Dead letter handling
- Duplicate detection
Example: Order Processing System
// Send order to queue
public class OrderService
{
private readonly QueueClient _queueClient;
public async Task SubmitOrderAsync(Order order)
{
var message = new ServiceBusMessage(
JsonSerializer.Serialize(order))
{
ContentType = "application/json",
MessageId = order.OrderId.ToString(),
Subject = "NewOrder",
ReplyTo = "order-response"
};
// Enable duplicate detection
message.ApplicationProperties["DueDate"] = order.DueDate;
await _queueClient.SendMessageAsync(message);
}
}
// Process order with sessions
public class OrderProcessor
{
public async Task ProcessMessageAsync(
ServiceBusReceivedMessage message,
ServiceBusMessageSession session)
{
var order = JsonSerializer.Deserialize<Order>(
message.Body.ToString());
try
{
await ValidateOrderAsync(order);
await ProcessPaymentAsync(order);
await ReserveInventoryAsync(order);
await session.CompleteAsync(message.LockToken);
// Send response to reply queue
await SendOrderConfirmationAsync(order);
}
catch (Exception ex)
{
// Dead letter if max retries exceeded
await message.DeadLetterAsync(
reason: ex.Message,
errorDescription: ex.StackTrace);
}
}
}
When to Choose Event Grid
Use Event Grid for:
-
Event-driven triggers
- Resource state changes
- Database changes
- Container lifecycle
-
Serverless workflows
- Logic Apps triggers
- Azure Functions triggers
- Webhook notifications
-
Fan-out patterns
- Multiple handlers per event
- Cross-region replication
- Audit logging
-
Operational events
- Cost alerts
- Security notifications
- Deployment events
Example: Cloud Resource Automation
// Subscribe to blob creation events
public class BlobEventHandler
{
[FunctionName("ProcessUploadedBlob")]
public async Task Run(
[EventGridTrigger] EventGridEvent eventGridEvent)
{
var blobUrl = eventGridEvent.Data["url"]?.ToString();
// Process based on blob type
if (blobUrl.Contains("orders/"))
{
await ProcessOrderFileAsync(blobUrl);
}
else if (blobUrl.Contains("images/"))
{
await ResizeImageAsync(blobUrl);
}
}
}
// Subscribe to Cosmos DB changes
public class CosmosChangeHandler
{
[FunctionName("HandleCosmosChange")]
public async Task Run(
[CosmosDBTrigger(
databaseName: "orders",
collectionName: "items",
ConnectionStringSetting = "CosmosConnection",
LeaseCollectionName = "leases")]
IReadOnlyList<Document> documents)
{
foreach (var doc in documents)
{
var changeType = doc.GetPropertyValue<string>("operationType");
if (changeType == "create")
{
await NotifyNewOrderAsync(doc);
}
else if (changeType == "update")
{
await UpdateOrderStatusAsync(doc);
}
}
}
}
Hybrid Patterns
Combined Architecture Pattern
┌─────────────────────────────────────────────────────────────────────┐
│ HYBRID EVENT ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ IoT Devices │──────┐ │
│ └──────────────┘ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Event Hubs │ (High-throughput ingestion) │
│ │ (Telemetry) │ │
│ └───────┬────────┘ │
│ │ │
│ ┌────────────┴────────────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Stream │ │ Event Grid │ (Real-time triggers) │
│ │ Analytics │ │ │ │
│ │ (Batch) │ │ • Alerts │ │
│ └─────────────┘ │ • Auditing │ │
│ └───────┬─────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Service Bus │ │ Logic Apps │ │
│ │ (Queued │ │ (Workflows) | │
│ │ Processing)│ └─────────────┘ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Order │ │
│ │ Processor │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Implementation Example
// Layer 1: Event Hubs - High-volume ingestion
public class TelemetryIngestion
{
private readonly EventHubProducerClient _eventHub;
public async Task IngestTelemetryAsync(DeviceData data)
{
var eventData = new EventData(
Encoding.UTF8.GetBytes(JsonSerializer.Serialize(data)));
eventData.Properties["Timestamp"] = DateTime.UtcNow.ToString("O");
eventData.Properties["DeviceId"] = data.DeviceId;
await _eventHub.SendAsync(new[] { eventData });
}
}
// Layer 2: Event Grid - Real-time triggers
public class RealTimeTrigger
{
// Automatically triggered when Event Hub captures data
[FunctionName("ProcessRecentData")]
public async Task Run(
[EventGridTrigger] EventGridEvent evt)
{
// Get data from capture
var blobUrl = evt.Data["fileUrl"]?.ToString();
// Trigger alert if anomaly detected
await CheckAnomalyAndAlertAsync(blobUrl);
}
}
// Layer 3: Service Bus - Reliable processing
public class ReliableProcessor
{
private readonly QueueClient _queueClient;
// Function triggered by Event Grid
public async Task QueueForProcessingAsync(ProcessRequest request)
{
var message = new ServiceBusMessage(
JsonSerializer.Serialize(request))
{
MessageId = Guid.NewGuid().ToString(),
Subject = "ProcessRequest",
ScheduledEnqueueTime = DateTimeOffset.UtcNow.AddSeconds(30)
};
await _queueClient.SendMessageAsync(message);
}
// Background processor
public async Task ProcessQueueMessageAsync(
ServiceBusReceivedMessage message)
{
var request = JsonSerializer.Deserialize<ProcessRequest>(
message.Body.ToString());
await PerformProcessingAsync(request);
await message.CompleteAsync(message.LockToken);
}
}
Cost Comparison
Pricing Models
| Service | Pricing Components | Free Tier |
|---|---|---|
| Event Hubs | Throughput units + Ingress | 1M events/month |
| Service Bus | Operations (premium: namespace) | 13M ops/month |
| Event Grid | Operations | 25K events/month |
Cost Optimization Strategies
| Service | Strategy |
|---|---|
| Event Hubs | Use reserved capacity, enable Capture for batch processing |
| Service Bus | Use sessions efficiently, implement dead letter cleanup |
| Event Grid | Filter events at subscription, use domain topics |
Example Cost Scenarios
| Scenario | Event Hubs | Service Bus | Event Grid |
|---|---|---|---|
| 100K events/day | ~$11/mo | ~$5/mo | ~$3/mo |
| 1M events/day | ~$75/mo | ~$45/mo | ~$25/mo |
| 10M events/day | ~$650/mo | ~$400/mo | ~$200/mo |
Migration Between Services
From Event Hubs to Service Bus
Use when switching from streaming to queue-based processing:
// Before: Event Hubs
await eventHubClient.SendAsync(eventData);
// After: Service Bus
var message = new ServiceBusMessage(Encoding.UTF8.GetBytes(payload))
{
MessageId = Guid.NewGuid().ToString()
};
await queueClient.SendMessageAsync(message);
From Event Grid to Event Hubs
Use when you need persistent event storage:
// Before: Event Grid (transient)
// Handler processes and discards
// After: Event Hubs (persistent)
await eventHubClient.SendAsync(new[] { eventData });
// Events retained for replay
Best Practices Summary
| Scenario | Recommended Service |
|---|---|
| IoT telemetry | Event Hubs |
| Order processing | Service Bus |
| Resource monitoring | Event Grid |
| Real-time analytics | Event Hubs |
| Long-running workflows | Service Bus + Logic Apps |
| Cross-region replication | Event Grid |
| Data pipeline triggers | Event Grid + Event Hubs |
Azure Integration Hub - Advanced Level