Queues — Cheat Sheet
RabbitMQ · 6 topics. Download the PDF or the Instagram carousel and share it.
Queue Declaration & Properties
Queues are declared with durable, exclusive, auto-delete, and argument flags; idempotent declaration ensures the queue exists before producers publish.
- ✓Queue declaration is idempotent; re-declaring with different properties raises a channel error
- ✓durable=true survives broker restart; durable=false (transient) is lost on restart
- ✓exclusive=true limits the queue to the declaring connection; deleted when connection closes
- ✓auto-delete=true deletes the queue when the last consumer disconnects
- ✓Arguments (x-message-ttl, x-max-length, x-dead-letter-exchange) add extended behaviour
- ✓RabbitAdmin in Spring auto-declares all Queue/Exchange/Binding beans at startup
@Configuration
public class QueueConfig {
// Standard durable work queue
@Bean
public Queue ordersQueue() {
return QueueBuilder.durable("orders.processing")
.withArgument("x-message-ttl", 600_000) // 10 min TTL
.withArgument("x-max-length", 50_000) // max 50k messages
.withArgument("x-dead-letter-exchange", "orders.dlx")
.build();
}
// Transient queue — lost on broker restart
@Bean
public Queue tempQueue() {
return QueueBuilder.nonDurable("orders.temp").build();
}
}Durable vs Transient Queues
Durable queues survive broker restart; transient queues are lost on restart. Combine durable queues with persistent message delivery mode for full data safety.
- ✓Durability = queue survives restart (declaration persisted); persistence = message survives restart (delivery-mode=2)
- ✓Both must be set together for full data safety — one without the other still risks data loss
- ✓Transient queues are faster (no disk I/O) and suit ephemeral patterns like RPC reply queues
- ✓Classic durable queues have a brief data-loss window under power failure — quorum queues do not
- ✓Quorum queues (3.8+) use Raft replication across nodes; recommended for business-critical data
- ✓Quorum queues do not support per-message TTL, exclusive, or non-durable configurations
@Bean
public Queue ordersDurableQueue() {
return QueueBuilder.durable("orders.processing").build();
// durable=true by default in QueueBuilder.durable()
}
// Publishing with persistent delivery mode
@Autowired RabbitTemplate rabbitTemplate;
public void publishOrder(OrderEvent event) {
rabbitTemplate.convertAndSend("orders", "order.created", event, message -> {
message.getMessageProperties()
.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return message;
});
}Exclusive & Auto-Delete Queues
Exclusive queues are accessible by a single connection and deleted when it closes; auto-delete queues are deleted when the last consumer unsubscribes — ideal for temporary reply queues.
- ✓Exclusive queue: only the declaring connection can use it; deleted when that connection closes
- ✓Auto-delete queue: deleted when the last consumer unsubscribes (must have had at least one)
- ✓RPC reply queues are the canonical use case for exclusive + auto-delete queues
- ✓Auto-delete fanout subscriber queues prevent stale message accumulation for absent subscribers
- ✓Exclusive queues cannot be shared across connections — use durable queues for shared consumers
- ✓An auto-delete queue that never had a consumer is NOT automatically deleted
// RPC client — declare a unique exclusive reply queue
String replyQueueName = channel.queueDeclare().getQueue(); // auto-named, exclusive, auto-delete
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.correlationId(UUID.randomUUID().toString())
.replyTo(replyQueueName)
.build();
channel.basicPublish("", "rpc.requests", props, request.getBytes());
// Spring AMQP RPC — RabbitTemplate handles reply queue automatically
Object response = rabbitTemplate.convertSendAndReceive(
"rpc.requests", requestPayload);Bindings & Routing Keys
A binding connects an exchange to a queue with an optional routing key; a queue can have multiple bindings from different exchanges to receive diverse message streams.
- ✓A binding is a rule: exchange + routing key pattern → queue
- ✓Direct exchange: exact routing key match; Topic exchange: * (one word), # (zero or more)
- ✓Fanout exchange ignores routing keys and delivers to all bound queues
- ✓One queue can have multiple bindings — from multiple exchanges or multiple routing keys
- ✓One routing key can route to multiple queues (multicast) on a direct/topic exchange
- ✓Bindings are declared in Spring AMQP via Binding beans and auto-applied by RabbitAdmin
@Configuration
public class BindingConfig {
@Bean DirectExchange ordersExchange() { return new DirectExchange("orders"); }
@Bean Queue newOrderQueue() { return QueueBuilder.durable("orders.new").build(); }
@Bean Queue cancelQueue() { return QueueBuilder.durable("orders.cancel").build(); }
// Exact-match binding: routing key "order.created" → orders.new
@Bean
Binding newOrderBinding(Queue newOrderQueue, DirectExchange ordersExchange) {
return BindingBuilder.bind(newOrderQueue).to(ordersExchange).with("order.created");
}
// Exact-match binding: routing key "order.cancelled" → orders.cancel
@Bean
Binding cancelBinding(Queue cancelQueue, DirectExchange ordersExchange) {
return BindingBuilder.bind(cancelQueue).to(ordersExchange).with("order.cancelled");
}
}Message Properties
AMQP properties include delivery-mode (persistent/transient), content-type, correlation-id, reply-to, expiration, headers, and priority — all set by the producer per message.
- ✓delivery-mode=2 (PERSISTENT) is required for messages to survive broker restart
- ✓correlation-id + reply-to together implement the RPC/request-reply pattern
- ✓expiration is per-message TTL in milliseconds as a String (independent of queue x-message-ttl)
- ✓headers is a Map<String,Object> for arbitrary application-level metadata
- ✓priority (0-9) is honoured only if the queue was declared with x-max-priority argument
- ✓Use @Header annotation in @RabbitListener to extract individual properties cleanly
rabbitTemplate.convertAndSend("orders", "order.created", event, message -> {
MessageProperties props = message.getMessageProperties();
props.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // survive restart
props.setContentType("application/json");
props.setMessageId(UUID.randomUUID().toString());
props.setCorrelationId(requestContext.getTraceId());
props.setExpiration("60000"); // 60s per-message TTL
props.setHeader("source-service", "order-service");
props.setHeader("schema-version", "2");
return message;
});Message Persistence
Set delivery_mode=2 (persistent) on messages AND use a durable queue to survive broker restart; fsync overhead makes persistent messages slower than transient ones.
- ✓Both queue durability AND message delivery_mode=2 are required — one without the other is not persistence.
- ✓Persistent messages add latency because RabbitMQ must fsync before acknowledging; batch fsyncing mitigates this.
- ✓Publisher confirms provide at-least-once delivery: only send after the broker confirms disk write.
- ✓Classic mirrored queues are deprecated since RabbitMQ 3.9; use Quorum queues for replicated persistence.
- ✓Quorum queues require a majority of replicas to be available — plan your cluster size (3 or 5 nodes) accordingly.
- ✓For very high throughput, consider separating persistent critical queues (payments) from transient queues (logs) on different vhosts.
@Configuration
public class RabbitConfig {
// durable=true: queue survives broker restart
// autoDelete=false: not deleted when last consumer disconnects
@Bean
public Queue ordersQueue() {
return QueueBuilder.durable("orders.queue")
.withArgument("x-queue-type", "quorum") // Recommended: quorum queue
.build();
}
}
// Publishing a persistent message
rabbitTemplate.convertAndSend("orders.exchange", "new-order", event, message -> {
message.getMessageProperties()
.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // delivery_mode=2
return message;
});
// Or use the built-in MessagePostProcessor shorthand
rabbitTemplate.convertAndSend("orders.exchange", "new-order",
event, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message msg) {
msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return msg;
}
});