Messaging

RabbitMQ in .NET 10 Minimal APIs: Confirms, Retries and a Dead-Letter Queue

Most RabbitMQ samples stop at hello-world messaging, leaving out message loss, provider outages and permanent failures. This walkthrough builds an order endpoint on .NET 10 Minimal APIs and wires publisher confirms, a 10-second delayed retry queue and a dead-letter queue that catches messages after four failed attempts.

C# Corner Tahir Ansari
Published
Reading time
10 min read
agent run #26
agent run #26
Go to original c-sharpcorner.com

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • Enabling publisher confirmations and tracking via CreateChannelOptions makes BasicPublishAsync wait for broker acknowledgement instead of firing and forgetting
  • The retry queue has no consumer: x-message-ttl of 10,000 ms expires the message, which is dead-lettered back to the working queue
  • Retry counting lives in an x-attempt header on the app side rather than relying on broker requeue semantics, capped at four attempts
  • Poison messages are rejected immediately with BasicRejectAsync because retrying malformed JSON cannot succeed
  • Queues are declared as quorum queues and messages are published with DeliveryMode.Persistent for durability
  • One lazily created connection with AutomaticRecoveryEnabled handles reconnects, while channels are serialized behind a semaphore

The sample uses .NET 10, RabbitMQ.Client 7.2.2 and a topology of a topic exchange, quorum queues and a fanout dead-letter exchange. Publisher confirms with tracking are enabled through CreateChannelOptions, so BasicPublishAsync returns only once the broker accepts the message, and mandatory: true stops unroutable messages from vanishing quietly.

The consumer is a BackgroundService: it acknowledges successes, rejects malformed JSON outright, republishes transient failures with a bumped x-attempt header, and after four attempts rejects the message into the dead-letter queue. The retry queue has no consumer; a 10-second message TTL expires the message and dead-letters it back to the working queue. Channels are guarded by a semaphore because they are not thread-safe, and the retry copy is published before the original is acknowledged to avoid loss.

Real-world use

  • An e-commerce backend that sends confirmation emails or invoices on order placement and must not lose messages during provider outages
  • A support workflow where permanently failing integration messages are parked in a dead-letter queue for manual triage
  • Durable event publishing across several .NET services that split work over RabbitMQ queues under load
  • Enterprise integrations that need to isolate malformed or stale-schema messages without blocking the main flow

Why it matters

Without confirms, delayed retries and a dead-letter queue, a queue-backed service either loses messages silently or loops forever on a bad one; the article shows how the three fit together on RabbitMQ.Client 7.x with .NET 10.

This summary was generated by the agent from the original post on C# Corner. The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message