Advanced Queue Types — Cheat Sheet
RabbitMQ · 5 topics. Download the PDF or the Instagram carousel and share it.
Priority Queues
Declare x-max-priority on a queue to enable priority levels (0–255); higher-priority messages are delivered first, allowing urgent tasks to skip ahead of normal work.
- ✓x-max-priority works only on classic queues — quorum queues do not support priority.
- ✓Keep x-max-priority ≤ 10; each priority level allocates an internal sub-queue even when empty.
- ✓Priority only matters under backlog — if consumers keep up, messages are still delivered FIFO.
- ✓Set consumer prefetch=1 when using priority queues to prevent high-priority messages being blocked behind pre-fetched low-priority ones.
- ✓For more than ~3 priority tiers, separate queues per tier with dedicated consumers is more predictable.
- ✓Monitor queue depth per-queue (not per-priority); there is no per-priority depth metric in RabbitMQ management.
@Configuration
public class PriorityQueueConfig {
// Declare priority queue with max 10 priority levels (0–10)
// Keep x-max-priority small (≤10) — each level needs its own internal queue
@Bean
public Queue orderPriorityQueue() {
return QueueBuilder.durable("orders.priority")
.withArgument("x-max-priority", 10)
.build();
}
}
// Publisher: set priority per message
@Service
public class OrderPublisher {
@Autowired
private RabbitTemplate rabbitTemplate;
public void publish(OrderEvent event, int priority) {
rabbitTemplate.convertAndSend(
"orders.exchange", "order.priority", event,
message -> {
message.getMessageProperties().setPriority(priority);
return message;
}
);
}
public void publishUrgent(OrderEvent event) {
publish(event, 10); // highest priority — jumps the queue
}
public void publishNormal(OrderEvent event) {
publish(event, 5); // medium
}
public void publishBulk(OrderEvent event) {
publish(event, 1); // lowest priority
}
}Quorum Queues
Quorum queues use the Raft consensus algorithm for data safety with leader election; they offer stronger durability guarantees than classic mirrored queues in clustered setups.
- ✓Quorum queues use Raft consensus — a write is acknowledged only when a majority of nodes persist it, preventing data loss.
- ✓Quorum queues replace deprecated classic mirrored queues; they eliminate split-brain scenarios on network partitions.
- ✓Always durable by design — non-durable quorum queues are not supported.
- ✓x-delivery-limit on a quorum queue automatically dead-letters messages that are nacked and requeued too many times — built-in poison message protection.
- ✓Trade-off: quorum queues have higher write latency (Raft round-trip) and more memory usage than non-replicated classic queues.
- ✓For production clusters, default to quorum queues for any queue that holds data you cannot afford to lose.
// Low-level AMQP client
Channel channel = connection.createChannel();
Map<String, Object> args = new HashMap<>();
args.put("x-queue-type", "quorum");
args.put("x-quorum-initial-group-size", 3); // default: use 3 nodes
// Quorum queues are always durable=true — non-durable is not supported
channel.queueDeclare("orders-quorum", true, false, false, args);
// Spring AMQP
@Bean
public Queue ordersQuorumQueue() {
return QueueBuilder.durable("orders-quorum")
.quorum() // sets x-queue-type=quorum
.quorumInitialGroupSize(3)
.build();
}
# OR via rabbitmq.conf — make quorum the default policy
# Match all queues with a policy:
rabbitmqctl set_policy quorum-queues ".*" \
'{"queue-mode":"default","x-queue-type":"quorum"}' \
--apply-to queuesLazy Queues
Lazy queues write messages to disk immediately, reducing memory usage for deep queues; ideal when consumers are slow and a large backlog must be held without crashing the broker.
- ✓Lazy queues write messages to disk immediately, keeping RAM free for other operations.
- ✓Default queues keep messages in RAM and page to disk only under memory pressure.
- ✓The memory watermark (default 40% RAM) triggers producer blocking — lazy queues help avoid it.
- ✓In RabbitMQ 3.12+, classic queues are lazy by default.
- ✓Apply laziness via x-queue-mode=lazy declaration argument or a policy (no restart needed).
- ✓For new HA deployments, prefer quorum queues; use lazy classic queues for large backlog scenarios.
// Spring AMQP — declare a lazy queue
@Bean
public Queue lazyOrderQueue() {
return QueueBuilder.durable("orders.processing")
.lazy() // x-queue-mode=lazy
.build();
}
// Or manually via arguments
@Bean
public Queue lazyQueue() {
return QueueBuilder.durable("bulk-exports")
.withArgument("x-queue-mode", "lazy")
.build();
}
# Apply lazy mode to existing queues via management API or CLI
# (no restart required — takes effect for new messages)
rabbitmqctl set_policy lazy-queues ".*" \
'{"queue-mode":"lazy"}' \
--apply-to queues
# RabbitMQ 3.12+ — classic queues are lazy by default
# Explicitly set to default (in-memory) mode:
@Bean
public Queue defaultModeQueue() {
return QueueBuilder.durable("hot-path")
.withArgument("x-queue-mode", "default") // in-memory (legacy fast mode)
.build();
}RabbitMQ Streams
RabbitMQ Streams (3.9+) provide a persistent, append-only log akin to Kafka topics; multiple consumers can read from any offset without deleting messages on acknowledgement.
- ✓Streams are append-only logs — acknowledged messages are NOT deleted; retention is by age or total size.
- ✓Each consumer independently tracks its own offset — replay, fan-out, and concurrent reads all work without interference.
- ✓The dedicated stream protocol (port 5552) outperforms AMQP for high-throughput stream consumers.
- ✓Named consumers with autoTrackingStrategy persist offsets server-side — safe for consumer restarts.
- ✓Streams require the stream_queue feature flag (RabbitMQ 3.9+) and rabbitmq_stream plugin enabled.
- ✓Use streams for fan-out to many consumers; use classic queues for competing-consumers (work queue) patterns.
<!-- pom.xml -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>stream-client</artifactId>
<version>0.15.0</version>
</dependency>
// Declare stream via AMQP (standard RabbitTemplate)
@Bean
public Queue orderEventStream() {
return QueueBuilder.durable("order-events-stream")
.withArgument("x-queue-type", "stream")
.withArgument("x-max-length-bytes", 10_000_000_000L) // 10 GB max
.withArgument("x-max-age", "7D") // 7-day retention
.build();
}
// Publishing via standard AMQP (same as classic queue)
rabbitTemplate.convertAndSend("", "order-events-stream", event);
// OR use dedicated stream Environment for high throughput
Environment env = Environment.builder()
.host("localhost")
.port(5552)
.build();
Producer producer = env.producerBuilder()
.stream("order-events-stream")
.build();
producer.send(
producer.messageBuilder()
.addData(serialize(event))
.properties().messageId(UUID.randomUUID().toString())
.messageBuilder().build(),
confirmationStatus -> {
if (!confirmationStatus.isConfirmed()) {
log.warn("Message not confirmed");
}
}
);Delayed Message Exchange
The community delayed-message plugin stores messages and delivers them after a configurable x-delay (ms), enabling scheduled tasks and retry-after-delay patterns.
- ✓x-delayed-message exchange holds messages internally until x-delay ms elapses, then routes normally
- ✓Requires the community rabbitmq_delayed_message_exchange plugin — not bundled by default
- ✓Delayed messages are stored on a single node — NOT replicated; node failure loses pending delays
- ✓For HA clusters, prefer the TTL+DLX retry pattern which uses standard replicated queues
- ✓x-delay header value is in milliseconds; maximum useful delay is limited by memory on the node
- ✓The underlying routing type (x-delayed-type: direct/topic/fanout) is set as a declaration argument
# Install the plugin on the broker
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
# Spring AMQP bean declaration
@Bean
public CustomExchange delayedExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct"); // underlying routing type
return new CustomExchange(
"orders.delayed", // exchange name
"x-delayed-message",// exchange type
true, // durable
false, // auto-delete
args
);
}
@Bean
public Queue scheduledOrderQueue() {
return QueueBuilder.durable("orders.scheduled").build();
}
@Bean
public Binding delayedBinding(Queue scheduledOrderQueue, CustomExchange delayedExchange) {
return BindingBuilder.bind(scheduledOrderQueue)
.to(delayedExchange).with("order.scheduled").noargs();
}