Introduction: Two Messaging Philosophies
Azure Service Bus and Event Hubs sit at adjacent seats in Microsoft’s messaging ecosystem, yet they answer fundamentally different questions about how to move data through distributed systems. This distinction is not cosmetic—it is rooted in competing models of what “a message” means, how messages flow, and what guarantees the system provides.
Service Bus was designed for transactional messaging: each message represents a unit of work that must be reliably processed exactly once, with strong ordering within a session, and with the ability to defer, schedule, or dead-letter problematic messages. Think of it as a reliable task queue with ACID-like guarantees.
Event Hubs was designed for high-throughput event streaming: messages (events) arrive in time-sequential order within a partition, but the consumer is expected to maintain its own read position. The system retains events for a configurable window (1–7 days by default), allowing replays and multi-consumer access patterns. Think of it as a distributed, durable append-only log.
This post unpacks both services from first principles, explores their architecture, compares protocols and consumer models, and provides a decision tree to match your workload to the right tool.
Part 1: Messaging Semantics—The Foundational Difference
Queue vs. Stream: Two Models of Message Delivery
At the most basic level, the two services implement different message delivery semantics.
Service Bus implements a queue paradigm. When a message arrives, Service Bus holds it until a consumer acknowledges receipt and successful processing. The message is then deleted. Only one consumer will ever receive that message; if two consumers are listening, the message is distributed to exactly one of them (load-balanced). Undelivered or problematic messages can be moved to a dead-letter queue for later inspection.
This model is transactional: a message is either fully processed or not processed at all. If a consumer crashes mid-processing, the message is automatically re-delivered to another consumer. There is no concept of a “log position” or “offset”—Service Bus tracks which messages belong to which consumer implicitly.
Event Hubs implements a stream (log) paradigm. Messages arrive in a partition in strict temporal order and are held for a retention window (default 24 hours, up to 7 days). Multiple consumers can simultaneously read the same events without blocking each other. Each consumer maintains its own read position (offset) in the log. When a consumer connects, it can start reading from the beginning, the end, or any specific offset.
This model is append-only and immutable: events are never deleted due to processing; they age out only after the retention window expires. Consumer failures have no effect on the log itself; the consumer simply resumes from its last recorded offset.
Why This Matters: Different Guarantees
| Aspect | Service Bus | Event Hubs |
|---|---|---|
| Delivery Model | Exactly-once (per consumer) | At-least-once (offset-driven) |
| Retention | Until consumed (with lock expiry) | Configurable time window (1–7 days) |
| Multi-consumer | Load-balanced distribution | Independent concurrent readers |
| Message Deletion | Explicit acknowledgment required | Age-based eviction |
| Ordering Guarantee | Per-session (if enabled) | Per-partition strict ordering |
Part 2: Partitioning and Throughput Architecture
Both services scale horizontally via partitioning, but the unit of scale and the pricing model differ significantly.
Service Bus: Messaging Units and Dedicated Tiers
Service Bus scales through Messaging Units (on Premium tier) or pricing tiers (Standard/Basic). A single Premium Service Bus namespace with 1 Messaging Unit provides:
- 1,000 operations/second throughput
- 1.2 GB/day of message flow
- Predictable latency (typically <1 ms for local reads)
Messaging Units are not directly tied to queues or topics. Instead, all entities (queues, topics, subscriptions) in a namespace share the throughput capacity. If you provision 4 Messaging Units, the entire namespace gets 4,000 ops/sec and 4.8 GB/day of capacity, distributed freely among all entities.
The critical detail: Service Bus does not have explicit partitioning at the message level. Within a queue or topic, messages are processed by the broker’s internal routing logic. However, if you enable Sessions, you get implicit partitioning: all messages with the same SessionId are guaranteed to be delivered in order and processed sequentially by a single consumer. Sessions act as a soft partition boundary.
Event Hubs: Throughput Units and Partition-First Design
Event Hubs scales through Throughput Units (TU). Each TU provides:
- 1,000 inbound events/second
- 1 MB/second inbound throughput
- 2 MB/second outbound throughput (for consumers)
- Automatic partitioning across multiple partitions (typically 4–32 partitions per TU)
Partitions are explicit and customer-visible. When you create an Event Hub, you specify the number of partitions (default 4, can scale 1–1024). Each partition is a serial log; events within a partition are strictly ordered by their enqueue sequence number. Events are distributed across partitions via a partition key (if provided) or round-robin (if not).
Each partition can be read independently. If you have 8 partitions and 8 consumers, each consumer can read one partition in parallel without contention. If you have 8 partitions and 1 consumer, that single consumer reads all partitions serially and maintains 8 separate offsets.
Throughput Comparison: Raw Numbers
At scale, Event Hubs typically handles higher inbound throughput per dollar and per compute resource. Service Bus Premium is optimized for latency and transactional guarantees over raw throughput. Here’s a rough comparison for a typical workload:
| Metric | Service Bus Premium (1 MU) | Event Hubs (1 TU) |
|---|---|---|
| Inbound throughput | ~1,000 msgs/sec | ~1,000 msgs/sec |
| Inbound bandwidth | 1.2 GB/day | 1 MB/sec = 86 GB/day |
| Multi-consumer penalty | None (load-balanced) | Per-partition bottleneck (serialization) |
| Typical p99 latency | <5 ms | <50 ms (multi-partition) |
Part 3: Consumer Patterns and Group Management
Service Bus: Competing Consumers and Sessions
A competing consumer pattern in Service Bus means multiple listeners (consumer instances) compete for messages from the same queue or subscription. Service Bus internally load-balances: if 5 messages arrive and 3 consumers are listening, Service Bus distributes them roughly evenly (e.g., 2, 2, 1).
When a consumer pulls a message from Service Bus, the message is immediately “locked” (hidden from other consumers) for a configurable lock duration (default 30 seconds). If the consumer calls Complete() or Acknowledge() before the lock expires, the message is removed. If the consumer crashes or the lock expires, the message is automatically re-delivered to another consumer (or back to the queue for retry).
Sessions add ordered processing. All messages with the same SessionId are grouped together and sent to the same consumer instance, in order. The lock mechanism still applies per-message. Sessions are useful for state-machine workflows: e.g., an order must be processed in sequence: “validate payment” → “reserve inventory” → “ship” → “confirm delivery”. Using a Session keyed by the order ID ensures this sequence is preserved.
Event Hubs: Consumer Groups and Offset Management
Event Hubs uses Consumer Groups (also called “Kafka consumer groups”). A consumer group is a named collection of consumers that read the same Event Hub. Each consumer in the group reads one or more partitions; the broker (or an external coordinator in Kafka mode) balances partitions across consumers.
Crucially, each consumer group maintains independent offsets. Two different consumer groups reading the same Event Hub can be at different positions. Group A might be at partition 0 offset 50,000; Group B might be at partition 0 offset 12,000. This enables multiple independent replay scenarios and auditing.
Offsets are stored in an external store: Azure Storage (Blob), an Azure Storage Account (Event Hubs Checkpoint Store), or Kafka’s internal __consumer_offsets topic (if using Kafka protocol). The consumer application is responsible for committing offsets after processing:
// Pseudo-code: Event Hubs consumer pattern
var consumer = new EventProcessorClient(checkpointStore, consumerGroup, ...);
consumer.ProcessEventAsync += async (args) => {
// Process event
await args.UpdateCheckpointAsync(); // Commit offset
};
If a consumer crashes, the next consumer in the group picks up from the last committed offset—no message is reprocessed (ideally) and none is skipped.
Redelivery and Failure Handling
Service Bus redelivery: If a message’s lock expires (consumer crash, timeout, or explicit lock renewal failure), the message is re-delivered. After a configurable number of redeliveries (max retry count, default unlimited), the message is moved to a dead-letter queue for inspection.
Event Hubs redelivery: There is no automatic redelivery. If a consumer processes an event but crashes before committing the offset, that event will be reprocessed the next time the consumer restarts (or a new consumer takes over the partition). The application must implement its own idempotency or exactly-once semantics using transaction logs or deduplication tables.
Part 4: Protocol Comparison—AMQP 1.0 vs. Kafka
Service Bus: AMQP 1.0 and the Advanced Message Queuing Protocol
Service Bus implements the Advanced Message Queuing Protocol (AMQP) 1.0, a standardized, open protocol for reliable messaging. AMQP provides:
- Link-based communication: A consumer establishes a link to a queue or subscription; messages flow over that link with explicit acknowledgment (accept, release, reject).
- Settlement semantics: Every message has settlement state: unsettled (in flight), accepted (committed to process), rejected (will not process), or released (return to queue).
- Transactions: AMQP 1.0 supports transaction identifiers, allowing a consumer to group multiple message acknowledgments or sends into a single atomic operation.
- Metadata: Rich message metadata including properties (content-type, correlation-id, reply-to), and application-defined properties.
A typical AMQP interaction:
- Consumer opens a link to
queue/myqueue - Service Bus sends a message to the link
- Consumer processes the message and sends an “accept” frame
- Service Bus acknowledges the settlement and deletes the message
- Consumer sends another “grant credit” to receive more messages
AMQP is not a streaming protocol. It is request-response oriented and optimized for transactional, exactly-once scenarios.
Event Hubs: Kafka Protocol and the Modern Event Streaming Standard
Event Hubs exposes a Kafka-compatible protocol endpoint (in addition to its native HTTPS/AMQP interface). Kafka is a distributed, append-only log protocol optimized for high-throughput streaming:
- Fetch-based pulling: Consumers actively pull messages in batches using the Fetch API, specifying start offset and batch size.
- Offset management: Consumers explicitly manage offsets (the position in the log) and commit them for durability.
- Consumer groups and rebalancing: Multiple consumers in a group automatically coordinate which partitions each will read, rebalancing if consumers join/leave.
- Compression and batching: Kafka protocols support multiple compression formats (snappy, lz4, gzip) and message batching for efficiency.
A typical Kafka consumer interaction:
- Consumer joins a consumer group
- Broker rebalances: assigns each consumer a subset of partitions
- Consumer calls
FetchConsumerGroup(start_offset, max_bytes) - Broker returns a batch of messages from the assigned partitions
- Consumer processes the batch and commits offset
- Consumer repeats from step 3
Kafka is inherently a streaming protocol. It assumes high-volume, replay-capable workloads.
Latency and Throughput Tradeoffs
| Aspect | AMQP (Service Bus) | Kafka (Event Hubs) |
|---|---|---|
| Latency | Sub-millisecond (strict queue) | 10–100 ms (batch-oriented) |
| Throughput | Good (optimized for messaging) | Excellent (optimized for streaming) |
| Message Size | Smaller messages (KB) | Larger batches (KB–MB) |
| Ordering Guarantee | Per-consumer or per-session | Per-partition strict ordering |
| Consumer Coordination | Server-side load balancing | Client-side consumer groups |
Part 5: Advanced Features
Service Bus: Dead-Lettering, Scheduled Delivery, and Message Deferral
Service Bus provides several features tailored to transactional workflows:
Dead-Letter Queue (DLQ): If a message is rejected, poisoned, or exceeds its max retry count, it is moved to the associated DLQ. The DLQ preserves the original message payload, metadata, and an auto-generated DeadLetterReason and DeadLetterErrorDescription. DLQs allow you to inspect failures post-mortem and requeue or remediate.
Scheduled Delivery: You can schedule a message to be delivered at a future time. The message is stored in Service Bus and automatically promoted to the active queue at the scheduled time. This is useful for delayed actions: e.g., “send a reminder email 24 hours after signup.”
Message Deferral: A consumer can defer a message (instead of completing or dead-lettering it), leaving it in the queue for later retrieval. The message is hidden from other consumers but can be explicitly retrieved by the same consumer using its sequence number.
Event Hubs: Capture to Storage and Replay
Event Hubs offers Capture, a built-in feature that automatically exports events to Azure Blob Storage in Avro or Parquet format, partitioned by time and partition. This creates a durable, queryable archive:
storage://container/namespace/eventhub/0/2026/04/17/10/00.avro
storage://container/namespace/eventhub/0/2026/04/17/10/05.avro
storage://container/namespace/eventhub/1/2026/04/17/10/00.avro
...
You can replay events by:
1. Reading the Avro/Parquet files from Blob Storage
2. Feeding them into a new Event Hub
3. Rewinding an existing consumer group’s offset to a prior timestamp
This pattern is essential for disaster recovery, data re-mining, and machine learning retraining workflows.
Part 6: Pricing and Cost Implications
Pricing is a major decision factor, especially at scale. Both services use a metered billing model, but the dimensions differ.
Service Bus Pricing (Premium Tier)
- Per Messaging Unit: $1.50/hour (approximately $1,080/month)
- Per-message operations: No per-operation charge; throughput is included in the MU capacity
- Outbound data transfer: Charges apply at standard Azure data egress rates ($0.02–$0.12 per GB, depending on region and destination)
- Brokered connections: Premium tier includes unlimited brokered connections
A typical scenario: 1 million messages/day with 1 KB payload.
– Throughput requirement: ~12 msgs/sec, well under 1 MU (1,000 msgs/sec)
– Cost: ~$1,080/month (1 MU) + $0.02 outbound (negligible if internal)
Event Hubs Pricing
- Per Throughput Unit: $0.076/hour (approximately $55/month)
- Capture to Storage: $0.10 per million captured events (approximately $3 for 1 million events)
- Outbound data transfer: Same as Service Bus
A typical scenario: 1 million messages/day with 1 KB payload, Capture enabled.
– Throughput requirement: ~12 msgs/sec, well under 1 TU (1,000 msgs/sec)
– Cost: ~$55/month (1 TU) + $3 Capture + $0.02 outbound = ~$58/month
At low volumes, Event Hubs is 10–20x cheaper. At very high volumes (10+ TUs or MUs), the pricing gap narrows, but the architectural difference becomes more important.
Part 7: Messaging Semantics Diagram

This diagram illustrates:
- Service Bus (left): A namespace with queues and topics, backing queues with session-based grouping, and exclusive locks per message.
- Event Hubs (right): A namespace with an Event Hub, partitions as explicit log-segments, partition keys determining distribution, and offsets as the unit of consumer state.
Part 8: Consumer Model Comparison

The diagram contrasts:
- Service Bus competing consumers: Three consumers listen to the same queue; Service Bus load-balances and locks messages (showing lock duration expiry and re-delivery).
- Event Hubs consumer group: Three consumers read partitions independently, each maintaining its own offset checkpoint.
Part 9: Throughput Unit vs. Messaging Unit Scaling

This diagram shows:
- Service Bus scaling: Adding Messaging Units increases the entire namespace’s capacity uniformly; all queues share the pool.
- Event Hubs scaling: Adding Throughput Units increases partition count and inbound/outbound capacity; partitions are an explicit scaling dimension.
Part 10: Protocol Layering

This diagram illustrates:
- AMQP 1.0 (Service Bus): A request-response protocol with settlement frames, transactions, and link-based messaging.
- Kafka (Event Hubs): A fetch-based protocol with consumer groups, offset commits, and group coordination.
Part 11: Decision Tree

Navigate using these questions:
-
Do you need exactly-once processing guarantees?
– Yes → Service Bus (built-in settlement semantics)
– No → Proceed to Q2 -
Is throughput > 1,000 msgs/sec per partition expected?
– Yes → Event Hubs (optimized for streaming)
– No → Proceed to Q3 -
Do you need multi-consumer replays of the same events?
– Yes → Event Hubs (retention window, independent consumer groups)
– No → Proceed to Q4 -
Do messages form logical sequences (e.g., order events)?
– Yes → Service Bus (Sessions for ordering)
– No → Proceed to Q5 -
Is cost a primary constraint and volume is low (<1,000 msgs/sec)?
– Yes → Event Hubs (lower base cost at small scale)
– No → Proceed to Q6 -
Do you require scheduled/deferred delivery?
– Yes → Service Bus (built-in scheduling, deferral)
– No → Either service, consider organizational preferences and existing tooling
Part 12: The Role of Event Grid
Azure’s ecosystem includes a third service, Event Grid, which is easy to confuse with Event Hubs. Here’s the distinction:
Event Grid is a serverless event broker optimized for reactive, event-driven architectures. When a resource in Azure generates an event (e.g., a blob upload, a VM scale set instance change, a custom application event), Event Grid forwards it to registered subscribers (Functions, Queues, Service Bus Topics, Event Hubs, or custom webhooks). Event Grid does not retain events; it is a real-time fanout mechanism, not a message queue or stream.
Event Grid is the right choice when:
– You have multiple, unknown consumers that need near-instant notification
– Retention is not required
– You need to decouple event producers from consumers via a managed broker
Event Grid is not the right choice for:
– Ordered delivery across consumers (fanout is parallel)
– Replay or historical analysis (no retention)
– High-throughput streaming (optimized for notifications, not bulk data)
Part 13: Real-World Scenario: IoT Data Ingestion
Consider an IoT platform that ingests telemetry from 10,000 sensors, each sending data every 10 seconds (i.e., 1,000 events/second aggregate).
Requirements:
– Real-time alerts (when a sensor reading exceeds a threshold)
– Historical replay for ML retraining
– Multi-consumer analytics (separate teams analyzing the same data)
– Minimal latency for alerts
Using Event Hubs:
– 4 Throughput Units (supports 4,000 msgs/sec inbound; cost ~$220/month)
– 16 partitions (4 TU × 4 default partitions per TU)
– Partition key: sensor ID (distributes events across partitions)
– Consumer Group 1: Real-time alerting (Azure Stream Analytics)
– Consumer Group 2: Data lake archival (Azure Data Explorer)
– Consumer Group 3: ML retraining (Azure Databricks)
– Capture enabled: automatic Parquet export to blob storage (cost ~$300/month for 1 billion events/month)
– Total monthly cost: ~$520
Using Service Bus Topics + Subscriptions:
– 2 Messaging Units (cost ~$2,160/month)
– Topic: telemetry
– Subscription 1: alerts with SQL filter (alert > threshold)
– Subscription 2: datalake (all messages)
– Subscription 3: ml-retrain (all messages)
– Each subscription processes messages independently
– Total monthly cost: ~$2,160
Verdict: Event Hubs is 4x cheaper and better suited to streaming and replay. Service Bus adds unnecessary transactional overhead.
Part 14: Real-World Scenario: Order Processing with Transactional Guarantees
Consider an e-commerce platform that processes orders. Each order triggers a sequence of steps:
- Validate payment
- Reserve inventory
- Create shipment
- Send confirmation email
Failures at any step must trigger compensation (refund, re-stock, cancel shipment).
Requirements:
– Exactly-once processing (no double-charges or duplicate shipments)
– Ordered processing per order (must complete validation before reserving inventory)
– Dead-lettering of malformed orders
– Scheduled delivery of cancellation emails (48-hour delay for reconsideration)
Using Service Bus:
– Queue: orders
– Each message has SessionId = OrderID
– Message properties: ScheduledEnqueueTimeUtc for delayed emails
– Consumer: Orchestration function that:
1. Receives an order message
2. Validates payment (calls external API)
3. Calls CompleteAsync() only after all steps succeed
4. Unhandled exceptions trigger DeadLetterAsync()
5. Dead-lettered messages are monitored via DLQ
– 1 Messaging Unit (cost ~$1,080/month) handles 1 million orders/month
Using Event Hubs:
– Event Hub: orders
– Partition key: OrderID
– Capture enabled for audit trail
– Consumer Group: order-processing
– Custom application logic:
1. Consumer reads an event
2. Validates payment
3. Calls external inventory API
4. On success, manually commits offset
5. On failure, writes to a separate dead-letters topic in Service Bus (manual DLQ)
6. Scheduled email delivery must be implemented separately (e.g., schedule a Function)
– 1 Throughput Unit (cost ~$55/month) + 1 Service Bus Premium (for scheduled emails and DLQ, cost ~$1,080/month) = ~$1,135/month
Verdict: Service Bus is simpler and more cost-effective for transactional order processing. Event Hubs forces you to implement too much manually.
Part 15: Hybrid Architectures
In practice, many organizations use both services in tandem:
Event Hubs as the ingestion layer: High-throughput, durable log for raw events. Event Hub Capture archives events to Blob Storage for compliance and retraining. Consumer groups enable independent analysis pipelines (alerting, analytics, ML).
Service Bus as the orchestration layer: Topics and Subscriptions fan out events to downstream processors. Subscriptions with SQL filters route to specialized handlers. Dead-letter queues collect anomalies for triage.
Example: IoT + Orders + Notifications
[ IoT Sensors ] → [ Event Hubs ] → [ Event Hub Capture / Blob Storage ]
↓
[ Azure Stream Analytics ]
↓
[ Service Bus Topic: alerts ]
↙ ↓ ↘
[ SMS ] [ Email ] [ Incident ]
Function Function Queue
↓
[ Order Processor ]
↓
[ Notification ]
Service Bus
↓
[ Email/SMS ]
Functions
Part 16: Migration and Interoperability
If you’re currently using Service Bus and want to evaluate Event Hubs (or vice versa), consider:
Service Bus → Event Hubs: You lose exactly-once semantics and scheduled delivery. You must implement:
– Manual offset management (replacing automatic message settlement)
– Idempotency tables (replacing lock semantics)
– Custom scheduling (replacing ScheduledEnqueueTimeUtc)
Event Hubs → Service Bus: You lose high-throughput streaming and multi-consumer replay. You gain:
– Automatic message expiry and dead-lettering
– Built-in session ordering
– Simpler exactly-once guarantees
In transitional periods, bridge the two:
– Event Hubs → Azure Function → Service Bus Topic (adapt and fan out)
– Service Bus Topic → Azure Logic App → Event Hub (re-stream for archival)
Part 17: Summary and Recommendation Matrix
| Scenario | Service Bus | Event Hubs | Event Grid |
|---|---|---|---|
| Transactional order processing | ★★★★★ | ★☆☆☆☆ | ☆☆☆☆☆ |
| IoT telemetry ingestion | ★★☆☆☆ | ★★★★★ | ★☆☆☆☆ |
| Real-time stream analytics | ★★☆☆☆ | ★★★★★ | ★☆☆☆☆ |
| Event sourcing / audit logs | ★★★★☆ | ★★★★★ | ★☆☆☆☆ |
| Multi-consumer fanout | ★★★★☆ | ★★★★☆ | ★★★★★ |
| Scheduled/deferred work | ★★★★★ | ★☆☆☆☆ | ☆☆☆☆☆ |
| Lowest cost (low volume) | ★★☆☆☆ | ★★★★★ | ★★★★★ |
| Exactly-once guarantees | ★★★★★ | ★★☆☆☆ | ★★★☆☆ |
Conclusion
Azure Service Bus and Event Hubs are complementary, not interchangeable. Service Bus excels at transactional messaging—ensuring exactly-once delivery, preserving order within sessions, and providing a rich toolset for business logic (dead-lettering, scheduling, deferral). Event Hubs excels at streaming—ingesting high-volume events, retaining them for replay, enabling multi-consumer analysis, and integrating with the modern data ecosystem (Spark, Kafka, Parquet).
Choose Service Bus when your messages represent actions that must complete atomically. Choose Event Hubs when your data represents a sequence of immutable facts that multiple consumers may need to analyze in different ways.
And remember: they can coexist in the same architecture, each handling the layer for which it was designed.
