Quantcast
Channel: Active questions tagged retry-logic - Stack Overflow
Viewing all articles
Browse latest Browse all 950

What are best practices for implementing limited message retry with RabbitMQ and subsequent routing to a dead-letter queue?

$
0
0

I'm working on a message processing system using RabbitMQ. My goal is to retry failed messages after 30 seconds, up to 3 times. If the message still fails after the third attempt, it should be moved to a dead-letter queue (DLQ) for further manual investigation.

Here's how I approached it:

I declared three exchanges: main, retry, and dead-letter — and three corresponding queues. The main queue is bound to the main exchange and is configured with:

  • x-dead-letter-exchange: pointing to the retry exchange
  • x-dead-letter-routing-key: pointing to the retry queue

When a message fails processing, it is nacked with requeue: false, causing it to be dead-lettered into the retry queue.

The retry queue has:

  • x-message-ttl: 30 seconds
  • x-dead-letter-exchange: pointing back to the main exchange
  • x-dead-letter-routing-key: pointing to the main queue

Since the retry queue has no consumer, after the TTL expires, the message becomes dead and is forwarded back to the main exchange — effectively retrying the original message. This cycle repeats.

In the consumer, I inspect the x-death header to determine how many times the message has already been retried. If it has failed 3 times or more, I manually publish it to the dead-letter exchange and acknowledge it to prevent further retries.

This is the relevant part of the retry logic:

private async Task ProcessEventAsync(object model, BasicDeliverEventArgs ea){    var body = ea.Body.ToArray();    var message = Encoding.UTF8.GetString(body);    try    {        var retryCount = 0L;        if (ea.BasicProperties.Headers != null && ea.BasicProperties.Headers.TryGetValue("x-death", out var xDeathRaw))        { ... }        if (retryCount >= 3)        {            _logger.LogError("Message exceeded max retry count. Routing to dead-letter queue.");            await _channel.BasicPublishAsync(                exchange: _deadLetterExchangeName,                routingKey: _deadLetterQueueName,                body: body            );            await _channel.BasicAckAsync(ea.DeliveryTag, false);            return;        }        var notificationEvent = JsonSerializer.Deserialize<NotificationEvent>(message);        _logger.LogInformation("It contains {@NotificationEvent}", notificationEvent);        await _channel.BasicAckAsync(ea.DeliveryTag, false);    }    catch (Exception ex)    {        _logger.LogError("Processing failed. Will retry.");        await _channel.BasicNackAsync(ea.DeliveryTag, false, requeue: false);    }    await Task.Yield();}

Is this a good approach for implementing limited retries with RabbitMQ?Are there any cleaner, more idiomatic, or fault-tolerant patterns I should consider for this scenario?

Full code you can find here: https://github.com/kotenko2002/DLXtesting/blob/master/DLXtesting/Workers/NewWoker.cs


Viewing all articles
Browse latest Browse all 950

Trending Articles