Azure 33: Service Bus — Topics, Queues & Dead-Letter
easy⏱ 5 mincourseazure
Queue vs Topic
A Queue is a 1:1 pipe — many senders, but each message is delivered to exactly one consumer. A Topic is pub-sub — senders publish once, and every Subscription gets its own copy (optionally filtered by SQL or correlation rules). Use queues for work-stealing (one worker per job); use topics when multiple downstream services each need to react to the same event.
// Publish to a topic — fan-out to all subscriptions
var sender = client.CreateSender("order-events");
await sender.SendMessageAsync(new ServiceBusMessage(json) {
Subject = "order.created",
CorrelationId = orderId,
});
Peek-lock & redelivery
Default receive mode is PeekLock: the broker hands the message to the consumer but hides it under a lock (default 60s). On CompleteAsync() the message is deleted; on AbandonAsync() or lock expiry it's re-queued. After MaxDeliveryCount (default 10) the broker moves it to the Dead-Letter Queue (DLQ). This is the retry budget — an unrecoverable poison message doesn't block the queue forever.
await using var processor = client.CreateProcessor("orders");
processor.ProcessMessageAsync += async args => {
try { await handle(args.Message); await args.CompleteMessageAsync(args.Message); }
catch { await args.AbandonMessageAsync(args.Message); }
};
Sessions for ordering
Service Bus normally delivers messages to multiple consumers concurrently — order is not guaranteed. Enable sessions to pin all messages with the same SessionId to a single consumer, processed sequentially. Use cases: per-aggregate ordering (all events for orderId=42 go to the same worker), saga state machines.
Build a Service Bus topic simulator
Create a ServiceBusTopic with subscribe(name, filter) (filter is a predicate on subject) and publish(message) that fans out to matching subscriptions. Each subscription keeps a retry counter per message — if process() throws, the message goes back to the queue; after maxDeliveryCount=3 failures it moves to subscription.dlq. Log every publish, deliver, retry, and dead-letter event.
Always enable the DLQ dashboard
In production, messages in the DLQ are the single highest-signal indicator of a broken consumer. Wire an Azure Monitor alert on DeadletteredMessages > 0 for every critical subscription, and build a small ops tool to inspect and re-submit DLQ messages after fixes. Silent DLQs are how outages compound.
Quiz: Subscription filter
Your topic publishes order.created, order.paid, order.cancelled. An invoice service should only care about paid orders. What goes in the subscription filter? sys.Subject = 'order.paid' — an SQL filter on the subject routes only matching messages, leaving the rest to other subscriptions.