RabbitMQ: Message Acknowledgment (ACK, NACK)

Shivam DubeyShivam Dubey
4 min read

Message acknowledgment in RabbitMQ ensures reliable message delivery. It helps the broker determine whether a message has been processed successfully or needs to be resent.

This article covers:

  • What message acknowledgment is

  • Why it's important

  • Types of acknowledgments (ACK and NACK)

  • How to implement acknowledgments using RabbitMQ

  • A flowchart for visualization

  • Example code in Golang

๐Ÿง‘โ€๐Ÿ’ป What is Message Acknowledgment?

Message acknowledgment allows consumers to inform RabbitMQ whether a message was processed successfully.

  • ACK (Acknowledgment): Informs RabbitMQ that a message was successfully processed and can be removed from the queue.

  • NACK (Negative Acknowledgment): Informs RabbitMQ that the message processing failed and should be requeued or discarded.

๐Ÿ“Œ Why is it Important?

  • Ensures no message loss during failures

  • Prevents duplicate message processing

  • Provides fault-tolerant and reliable systems

๐Ÿ“Š Flowchart: How Acknowledgment Works in RabbitMQ

            +-------------------+
            |    Producer       |
            +-------------------+
                      |
                      โ†“
           +-----------------------+
           |       Exchange        |
           +-----------------------+
                      |
                      โ†“
                +---------+
                |  Queue  |
                +---------+
                      โ†“
                +------------+
                |  Consumer  |
                +------------+
                     /     \
             ACK   /       \   NACK
            +-------+      +-------+
            |Remove |      |Requeue|
            |Message|      |Message|
            +-------+      +-------+
  • ACK: Removes the message from the queue.

  • NACK: Returns the message to the queue for reprocessing.

๐ŸŸข Types of Acknowledgments

โœ… ACK (Acknowledgment)

  • Sent when a message is successfully processed.

  • RabbitMQ removes the message from the queue.

  • Prevents duplicate processing.

โ— NACK (Negative Acknowledgment)

  • Sent when message processing fails.

  • RabbitMQ can requeue the message for retry or discard it.

  • Useful for error handling and fault tolerance.

๐Ÿ”„ Reject

  • Similar to NACK but does not support bulk rejection.

  • Allows selective discarding of messages without requeuing.

๐Ÿ›  Example: Implementing Acknowledgment in Golang

๐Ÿ“ฆ Producer Code

The producer sends messages to a specified queue.

package main

import (
    "log"
    "github.com/streadway/amqp"
)

func main() {
    // Establish connection to RabbitMQ server
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close() // Ensure the connection is closed when the function exits

    // Open a channel for communication with RabbitMQ
    ch, err := conn.Channel()
    if err != nil {
        log.Fatal(err)
    }
    defer ch.Close() // Ensure the channel is closed when done

    // Declare a queue named "ack_queue"
    // Non-durable, non-auto-delete, non-exclusive, and no additional arguments
    q, err := ch.QueueDeclare("ack_queue", false, false, false, false, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Publish 5 messages to the queue
    for i := 0; i < 5; i++ {
        // Construct message body with a simple numbering
        body := "Message " + string(rune(i + '0'))

        // Publish the message to the queue
        err = ch.Publish(
            "",        // Exchange (empty means default exchange)
            q.Name,    // Routing key (queue name)
            false,     // Mandatory (false = discard message if no queue)
            false,     // Immediate (false = allow broker to queue the message)
            amqp.Publishing{
                ContentType: "text/plain",
                Body:        []byte(body),
            },
        )
        if err != nil {
            log.Printf("Failed to send message: %v", err)
        }
        // Log successful message publishing
        log.Printf(" [x] Sent %s", body)
    }
}

๐Ÿง‘โ€๐Ÿ’ป Consumer with ACK and NACK

The consumer reads messages and sends acknowledgments based on the processing result.

package main

import (
    "log"
    "github.com/streadway/amqp"
)

func main() {
    // Step 1: Establish connection to RabbitMQ server
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close() // Ensure the connection is closed when the function exits

    // Step 2: Open a channel for communication with RabbitMQ
    ch, err := conn.Channel()
    if err != nil {
        log.Fatal(err)
    }
    defer ch.Close() // Ensure the channel is closed when done

    // Step 3: Declare a queue named "ack_queue"
    // Non-durable, non-auto-delete, non-exclusive, and no additional arguments
    q, err := ch.QueueDeclare("ack_queue", false, false, false, false, nil)
    if err != nil {
        log.Fatal(err)
    }

    // Step 4: Consume messages from the queue
    msgs, err := ch.Consume(
        "ack_queue", "", false, false, false, false, nil,
    )
    if err != nil {
        log.Fatal(err)
    }

    // Step 5: Process and acknowledge messages
    for msg := range msgs {
        log.Printf("Received: %s", msg.Body)

        // Simulating error scenario for specific messages
        if string(msg.Body) == "Message 2" {
            log.Printf("NACK Message: %s", msg.Body)
            // Step 5a: Send NACK to requeue the message
            msg.Nack(false, true)
        } else {
            log.Printf("ACK Message: %s", msg.Body)
            // Step 5b: Send ACK to confirm successful processing
            msg.Ack(false)
        }
    }
}

๐Ÿ“ Explanation

  • msg.Ack(false): Confirms successful processing, removing the message.

  • msg.Nack(false, true): Rejects and requeues the message.

  • msg.Reject(false): Rejects without requeuing.

Key Arguments

  • false in msg.Ack(false): Acknowledges a single message.

  • true in msg.Nack(false, true): Requeues the message.

  • false in msg.Nack(false, false): Discards the message.

  • false in ch.Consume(auto-ack=false): Ensures manual acknowledgment.

โœ… Key Takeaways

  • Use ACK for reliable message processing.

  • Use NACK for error handling and retries.

  • Enable durable queues for message persistence.

  • Monitor unacknowledged messages to detect failures.

This ensures a robust message processing system using RabbitMQ.


0
Subscribe to my newsletter

Read articles from Shivam Dubey directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Shivam Dubey
Shivam Dubey