Understanding Message Acknowledgments
1. Sending Basic Ack
| Property | Description | Default Value |
|---|---|---|
| basic.ack | Used to acknowledge a message received from a queue | false |
| delivery_tag | Identifier for the message being acknowledged | None |
| multiple | If true, acknowledges all messages up to and including the specified delivery tag | false |
Example: Sending Basic Ack
const amqp = require('amqplib');
async function sendBasicAck() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'my-queue';
const msg = { id: 1, content: 'Hello World' };
await channel.assertQueue(queue, { durable: true });
await channel.sendToQueue(queue, Buffer.from(JSON.stringify(msg)), { persistent: true });
// Simulate receiving a message
const consumerTag = await channel.consume(queue, (msg) => {
console.log('Received:', msg.content.toString());
// Acknowledge the message
channel.ack(msg);
});
console.log('Message acknowledged');
}
sendBasicAck().catch(console.error);
2. Sending Basic Nack
The basic.nack method in RabbitMQ is used to negatively acknowledge a message, indicating that the consumer was unable to process the message successfully. This is particularly useful in scenarios where a message needs to be re-queued or sent to a dead-letter exchange.
| Field | Description | Values |
|---|---|---|
| delivery_tag | Identifier for the message being negatively acknowledged | uint64 |
| requeue | Indicates whether the message should be re-queued or not | true or false |
Example: Using basic.nack to requeue a message
const amqp = require('amqplib');
async function nackExample() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'my-queue';
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, (msg) => {
if (msg.content.toString() === 'error') {
// Re-queue the message
channel.nack(msg, false, true);
} else {
// Accept the message
channel.ack(msg);
}
});
}
nackExample().catch(console.error);
requeue parameter in basic.nack controls whether the message should be re-queued or sent to a dead-letter exchange. If set to false, the message is not re-queued and can be routed to a dead-letter exchange if configured.
3. Sending Basic Reject
When a consumer receives a message from RabbitMQ, it can either ack (accept) or nack (reject) the message. The basic.nack method allows a consumer to reject a message and optionally requeue it.
| Field | Description | Values |
|---|---|---|
| delivery_tag | Unique identifier for the message being rejected | uint64 |
| requeue | Whether to requeue the message for redelivery | true or false |
Example: Rejecting a Message
const amqp = require('amqplib');
async function rejectMessage() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'my-queue';
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, (msg) => {
if (msg.content.toString() === 'reject') {
// Reject and requeue the message
channel.nack(msg, true);
} else {
// Accept the message
channel.ack(msg);
}
});
}
rejectMessage().catch(console.error);
requeue is set to false, the message will be discarded or sent to a dead-letter exchange if configured.
4. Using Multiple Ack
Multiple acknowledgments allow a consumer to acknowledge multiple messages at once, improving efficiency when processing batches of messages.
| Property | Description | Values |
|---|---|---|
| multiple | Indicates whether to acknowledge multiple messages | true or false |
Example: Acknowledging Multiple Messages
const amqp = require('amqplib');
async function consumeMessages() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'task_queue';
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, (msg) => {
// Process the message
console.log('Received:', msg.content.toString());
// Acknowledge multiple messages at once
channel.ack(msg, true); // true = multiple acknowledgment
}, { noAck: false });
}
consumeMessages().catch(console.error);
multiple: true, the server acknowledges all messages up to and including the specified message. Ensure that all previous messages in the batch have been successfully processed before acknowledging.
5. Requeuing Messages
| Property | Description | Values |
|---|---|---|
| requeue | Controls whether a message should be requeued or discarded | true or false |
Example: Requeuing a Message
const amqp = require('amqplib');
async function requeueMessage() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'my-queue';
channel.consume(queue, (msg) => {
if (msg && msg.content) {
const data = JSON.parse(msg.content.toString());
console.log('Processing:', data);
// Simulate a processing error
if (data.error) {
console.log('Requeuing message...');
channel.nack(msg, false, true); // Requeue = true
} else {
console.log('Message processed successfully');
channel.ack(msg); // Acknowledge the message
}
}
});
}
requeueMessage().catch(console.error);
requeue is set to true, the message is returned to the queue and can be processed again by another consumer.
6. Understanding Unacked Messages
| Property | Description | Notes |
|---|---|---|
| unacked_messages | Number of messages that have been delivered but not yet acknowledged | Used for monitoring and debugging message flow |
| message_id | Unique identifier for each message | Used to track individual messages through the system |
| delivery_tag | Identifier for a specific message delivery | Used to acknowledge or reject specific messages |
Example: Tracking Unacked Messages
const amqp = require('amqplib');
async function trackUnackedMessages() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queueName = 'message-queue';
await channel.assertQueue(queueName, { durable: true });
// Set up message consumption
channel.consume(queueName, (msg) => {
console.log('Received message:', msg.content.toString());
// Do not acknowledge yet
}, { noAck: false });
// Monitor unacked messages (pseudo-code)
setInterval(() => {
const unackedCount = channel.unackedMessages || 0;
console.log(`Unacked messages: ${unackedCount}`);
}, 5000);
}
trackUnackedMessages().catch(console.error);
7. Understanding Redelivered Flag
The redelivered flag is a boolean property set by the broker when a message is redelivered to a consumer. It indicates whether the message has been previously delivered and acknowledged or not.
| Property | Description | Value |
|---|---|---|
| redelivered | Indicates if the message was previously delivered and not acknowledged. | true or false |
Example: Handling Redelivered Messages
const amqp = require('amqplib');
async function consumeMessages() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'my-queue';
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, (msg) => {
console.log('Redelivered:', msg.fields.redelivered);
if (msg.fields.redelivered) {
console.log('Message was previously redelivered');
}
// Process message
channel.ack(msg);
});
}
consumeMessages().catch(console.error);
nack with requeue set to true.8. Setting Ack Timeout
| Property | Values | Description |
|---|---|---|
| consumer.ack.timeout | 30000 |
Timeout in milliseconds for message acknowledgment |
| consumer.ack.timeout.mode | auto, manual |
Mode of acknowledgment timeout handling |
9. Handling Ack Failures
Example: Consumer handles nack with retry logic
const amqp = require('amqplib');
async function handleAckFailure() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'task_queue';
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, async (msg) => {
try {
// Process message
await processMessage(msg.content.toString());
// Acknowledge successful processing
channel.ack(msg);
} catch (error) {
console.error('Message processing failed:', error);
// Reject and requeue for retry
channel.nack(msg, false, true); // requeue = true
}
});
}
async function processMessage(data) {
// Simulate potential failure
if (Math.random() < 0.2) throw new Error('Random failure');
console.log('Processing:', data);
}
| Method | Description | Parameters | Notes |
|---|---|---|---|
| channel.nack(msg, allUpTo, requeue) | Rejects one or more messages | msg: Message objectallUpTo: Booleanrequeue: Boolean |
Use requeue = true to re-queue failed messagesNEW in AMQP 0-9-1 |
| channel.reject(msg, requeue) | Rejects a single message | msg: Message objectrequeue: Boolean |
Legacy method, DEPRECATED in favor of nack |
nack is called with requeue = true, the message returns to the queue and can be processed by another consumer.
Best Practices for Ack Failures
- Implement retry logic with exponential backoff
- Use dead-letter exchanges (DLX) for messages that fail too many times
- Log failed messages for debugging and monitoring
- Ensure consumers can handle re-queued messages idempotently
10. Recovering from Nack
When a message is rejected using nack(), it can be recovered in several ways depending on the configuration and use case.
| Recovery Method | Description | Configuration |
|---|---|---|
| requeue = true | Requeues the message back to the queue for re-processing. | nack(msg, true) |
| requeue = false | Discards the message or sends it to a dead-letter exchange (if configured). | nack(msg, false) |
Example: Requeuing a Message
channel.consume(queue, (msg) => {
if (msg.content.toString() === "error") {
// Requeue the message for later processing
channel.nack(msg, false); // or true to requeue
} else {
channel.ack(msg);
}
});
requeue is set to false, the message will be discarded or sent to a dead-letter exchange if one is configured.
11. Using Manual Ack Best Practices
| Best Practice | Description | Implementation |
|---|---|---|
| Acknowledge messages immediately after processing | Ensures no duplicate processing and maintains message order | |
| Use manual acknowledgment mode | Explicitly control when messages are acknowledged | |
| Handle errors gracefully | Requeue messages on failure to prevent data loss | |
| Avoid long-running operations | Prevent channel timeouts and reduce risk of message re-delivery | |
12. Monitoring Unacked Messages
Unacked messages are those that have been delivered to a consumer but not yet acknowledged. Monitoring these is crucial for ensuring message reliability and identifying potential bottlenecks or consumer issues.
| Property | Description | Value |
|---|---|---|
| unacked_messages | Number of messages currently unacknowledged in a queue | 0 |
| consumer_tag | Identifier for the consumer that received the message | consumer-1 |
| message_id | Unique identifier for the message | msg-12345 |
Example: Using RabbitMQ Management Plugin to View Unacked Messages
curl -u guest:guest http://localhost:15672/api/queues/%2f/my-queue
messages_unacknowledged as part of the queue statistics.
Example: Setting Up a Consumer with Manual Acknowledgment
const amqp = require('amqplib');
async function consumeMessages() {
const connection = await amqp.connect('amqp://localhost');
const channel = await connection.createChannel();
const queue = 'my-queue';
await channel.assertQueue(queue, { durable: true });
channel.consume(queue, (msg) => {
console.log('Received:', msg.content.toString());
// Manual acknowledgment
channel.ack(msg);
}, { noAck: false });
}
consumeMessages();