Skip to content

Widhian Bramantya

coding is an art form

Menu
  • About Me
Menu
rabbitmq

Dead Letter Queues in RabbitMQ: How to Handle Failed Messages

Posted on September 8, 2025September 8, 2025 by admin

Step 3: Consumer with Retry Counter

msgs, _ := ch.Consume("main_queue", "", false, false, false, false, nil)

for d := range msgs {
    retries := 0
    if val, ok := d.Headers["x-retry"].(int32); ok {
        retries = int(val)
    }

    log.Printf("Got: %s (retry=%d)", d.Body, retries)

    // simulate failure
    failed := true

    if failed {
        if retries < 2 { // <3 attempts total
            log.Println("Retrying...")
            // republish with incremented retry counter
            ch.Publish("", d.RoutingKey, false, false, amqp.Publishing{
                ContentType: "text/plain",
                Body:        d.Body,
                Headers:     amqp.Table{"x-retry": int32(retries + 1)},
            })
            d.Ack(false) // ack current, we already re-published
        } else {
            log.Println("Max retries reached → send to DLQ")
            d.Nack(false, false) // requeue=false → goes to DLQ
        }
    } else {
        d.Ack(false) // success
    }
}

Step 4: DLQ Consumer

msgsDLQ, _ := ch.Consume("dead_letters", "", true, false, false, false, nil)
for d := range msgsDLQ {
    log.Printf("[DLQ] %s", d.Body)
}

How It Works

  1. Producer sends message with x-retry=0.
  2. Consumer tries to process. If fail → republishes with x-retry=1.
  3. After 2 more failures (x-retry=2), the consumer rejects with requeue=false.
  4. RabbitMQ routes the message to DLQ.
  5. Operators can inspect DLQ to debug or retry later.

Best Practices

  • Use headers to track retries (RabbitMQ doesn’t track it automatically).
  • Delay retries (e.g., use a delay exchange or TTL queue) to avoid hot loops.
  • Alert on DLQ growth, if DLQ fills up, something is wrong upstream.
  • Separate DLQs per service, makes debugging easier.

Conclusion

Dead Letter Queues are a powerful way to handle failed messages in RabbitMQ.
By adding a retry mechanism with a counter, we can avoid sending messages to DLQ too early.
In our example, messages get 3 attempts:

  • 1st try + 2 retries → if still failing → goes to DLQ.

This design makes systems more resilient and gives developers a chance to fix transient issues before moving messages into permanent failure storage.

See also  High Availability in RabbitMQ: Clustering and Mirrored Queues Explained
Pages: 1 2
Category: RabbitMQ

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Linkedin

Widhian Bramantya

Recent Posts

  • Log Management at Scale: Integrating Elasticsearch with Beats, Logstash, and Kibana
  • Index Lifecycle Management (ILM) in Elasticsearch: Automatic Data Control Made Simple
  • Blue-Green Deployment in Elasticsearch: Safe Reindexing and Zero-Downtime Upgrades
  • Maintaining Super Large Datasets in Elasticsearch
  • Elasticsearch Best Practices for Beginners
  • Implementing the Outbox Pattern with Debezium
  • Production-Grade Debezium Connector with Kafka (Postgres Outbox Example – E-Commerce Orders)
  • Connecting Debezium with Kafka for Real-Time Streaming
  • Debezium Architecture – How It Works and Core Components
  • What is Debezium? – An Introduction to Change Data Capture
  • Offset Management and Consumer Groups in Kafka
  • Partitions, Replication, and Fault Tolerance in Kafka
  • Delivery Semantics in Kafka: At Most Once, At Least Once, Exactly Once
  • Producers and Consumers: How Data Flows in Kafka
  • Kafka Architecture Explained: Brokers, Topics, Partitions, and Offsets
  • Getting Started with Apache Kafka: Core Concepts and Use Cases
  • Security Best Practices for RabbitMQ in Production
  • Understanding RabbitMQ Virtual Hosts (vhosts) and Their Uses
  • RabbitMQ Performance Tuning: Optimizing Throughput and Latency
  • High Availability in RabbitMQ: Clustering and Mirrored Queues Explained

Recent Comments

  1. Playing with VPC AWS (Part 2) – Widhian's Blog on Playing with VPC AWS (Part 1): VPC, Subnet, Internet Gateway, Route Table, NAT, and Security Group
  2. Basic Concept of ElasticSearch (Part 3): Translog, Flush, and Refresh – Widhian's Blog on Basic Concept of ElasticSearch (Part 1): Introduction
  3. Basic Concept of ElasticSearch (Part 2): Architectural Perspective – Widhian's Blog on Basic Concept of ElasticSearch (Part 3): Translog, Flush, and Refresh
  4. Basic Concept of ElasticSearch (Part 3): Translog, Flush, and Refresh – Widhian's Blog on Basic Concept of ElasticSearch (Part 2): Architectural Perspective
  5. Basic Concept of ElasticSearch (Part 1): Introduction – Widhian's Blog on Basic Concept of ElasticSearch (Part 2): Architectural Perspective

Archives

  • October 2025
  • September 2025
  • August 2025
  • November 2021
  • October 2021
  • August 2021
  • July 2021
  • June 2021
  • March 2021
  • January 2021

Categories

  • Debezium
  • Devops
  • ElasticSearch
  • Golang
  • Kafka
  • Lua
  • NATS
  • Programming
  • RabbitMQ
  • Redis
  • VPC
© 2025 Widhian Bramantya | Powered by Minimalist Blog WordPress Theme