RabbitMQ Message Delivery Guarantees
RabbitMQ provides two primary approaches to ensure message delivery reliability:
- Consumer-side retry mechanisms using
@Retryableannotations with configurable attempts and intervals - Producer-side delivery confirmation techniques
Both methods may result in duplicate messages, requiring consumers to implement idempotent processing logic.
Producer-Side Reliability
RabbitMQ offers two mechanisms for controlling message delivery guarantees from producers:
- Exchange confirmation via ConfirmCallback
- Queue routing failure via ReturnCallback
Exchange Confirmation Mechanism
The message flow follows this path:
Producer → Broker → Exchange → Queue → Consumer
When enabled, producers asynchronously recieve acknowledgments from RabbitMQ after message delivery to exchanges.
Configuration Steps
spring:
rabbitmq:
publisher-confirm-type: correlated
Implementation Example
@Component
public class ExchangeConfirmCallback implements RabbitTemplate.ConfirmCallback {
@Override
public void confirm(CorrelationData correlationData,
boolean ack, String error) {
if (ack) {
System.out.println("Message delivered to exchange");
} else {
System.out.println("Delivery failed: " + error);
// Handle failure: retry or alert
}
}
}
Testing Configuration
@Configuration
public class MessagingConfig {
@Bean
public Queue demoQueue() {
return new Queue("demo_queue", true);
}
@Bean
public FanoutExchange demoExchange() {
return new FanoutExchange("demo_exchange");
}
@Bean
public Binding bindQueueToExchange() {
return BindingBuilder.bind(demoQueue())
.to(demoExchange());
}
}
Producer Implementation
@SpringBootTest
public class MessageSenderTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private ExchangeConfirmCallback confirmCallback;
@Test
public void sendMessage() {
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.convertAndSend("valid_exchange", "", "Test payload");
}
}
Queue Routing Failure Handling
Messages failing to route from exchanges to queues trigger return callbacks.
Configuration
spring:
rabbitmq:
publisher-returns: true
Mandatory Routing Setting
rabbitTemplate.setMandatory(true);
Implementation Example
@Component
public class QueueReturnCallback implements RabbitTemplate.ReturnCallback {
@Override
public void returnedMessage(Message message, int code,
String reason, String exchange,
String routingKey) {
System.out.println("Returned message: " + new String(message.getBody()));
System.out.println("Error code: " + code);
System.out.println("Exchange: " + exchange);
System.out.println("Routing key: " + routingKey);
// Handle undelivered messages
}
}
Testing Implementation
@SpringBootTest
public class MessageSenderTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private ExchangeConfirmCallback confirmCallback;
@Autowired
private QueueReturnCallback returnCallback;
@Test
public void sendWithInvalidRouting() {
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(returnCallback);
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.convertAndSend("valid_exchange",
"invalid_key",
"Test payload");
}
}
Consumer-Side Reliability
RabbitMQ provides three acknowledgment modes:
- NONE: No acknowledgment
- AUTO: Automatic acknowledgment
- MANUAL: Explicit acknowledgment
Manual Acknowledgment Configuration
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual
Consumer Implementation
@Component
@RabbitListener(queues = "demo_queue")
public class MessageProcessor {
@RabbitHandler
public void handleMessage(String payload, Channel channel,
Message message) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// Process message content
System.out.println("Received: " + payload);
// Acknowledge successful processing
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
// Reject with requeue
channel.basicNack(deliveryTag, false, true);
}
}
}
Note: basicNack allows batch rejection while basicReject handles single messages.