RabbitMQ: The Versatile Message Broker

Explore RabbitMQ's exchange-queue-binding model, routing patterns, dead letter queues, and how it compares to Kafka for different messaging workloads.

published: reading time: 41 min read author: GeekWorkBench updated: April 17, 2026
Quick Summary

RabbitMQ routes messages through exchanges to queues using direct, fanout, topic, and headers patterns, with support for dead letter queues and quorum queues when data loss isn't acceptable. The post covers backpressure, memory and disk alarms, cluster federation, the plugin ecosystem, and Spring AMQP integration. Delivery semantics range from at-most-once to exactly-once with a comparison to RabbitMQ Streams. After reading, you'll know whether RabbitMQ fits your use case better than Kafka and how to sidestep common configuration blunders.

RabbitMQ is one of the most widely deployed message brokers, known for its flexibility and mature ecosystem. Unlike Kafka’s log-based model, RabbitMQ uses a traditional queue-centric architecture with intelligent routing via exchanges. This makes it a natural fit for task queues, request/response patterns, and workloads that need fine-grained routing rules. The AWS SQS/SNS post covers managed alternatives, while the Publish/Subscribe Patterns post explains the routing concepts RabbitMQ builds on.

RabbitMQ: The Versatile Message Broker

Introduction

RabbitMQ is a message broker that implements the AMQP protocol. It sits between producers and consumers, routing messages through exchanges to queues. Unlike Kafka, RabbitMQ is broker-centric: queues store messages, exchanges route them.

Advanced Topics and Ecosystem

Shovel and Cluster Federation

Shovel and Cluster Federation Overview

RabbitMQ offers two plugins for moving messages between brokers: Shovel and Federation. They serve different use cases.

Shovel is a point-to-point replication tool. It connects to a source broker, consumes messages from a specific queue or exchange, and publishes them to a destination broker. Think of it as a directed pipeline: you control exactly what moves where. This makes Shovel ideal for disaster recovery setups where you want precise queue-to-queue replication, data migration between clusters, or selective forwarding of specific message flows.

Federation, by contrast, is a multi-source aggregation system. An upstream federation link pulls messages from multiple remote clusters into a local exchange or queue. The downstream cluster appears to consumers as if it has its own copy of the remote queues. Federation is better suited for geographic distribution, for example aggregating logs from regional data centers into a central cluster, or for building read-only replicas that serve consumers near their location.

DimensionShovelFederation
TopologyPoint-to-point (1:1)Multi-source (N:1)
ConfigurationPer-pipelinePer-upstream exchange/queue
LatencyPull model, configurableContinuous pull
Use caseMigration, DR, queue mirrorGeo-distribution, aggregation

Either plugin can be configured via the management UI, rabbitmqctl, or rabbitmq.conf. Choose Shovel when you need surgical precision over message movement. Choose Federation when you need automatic aggregation from many sources.

Shovel Plugin

Shovel moves messages between brokers. It connects to a source broker, consumes messages, and publishes them to a destination broker. This helps with disaster recovery, migration, and cross-datacenter replication.

# rabbitmq.conf - Shovel configuration
[shovel.
  destination_cluster].
  endpoint = cluster-b.internal
  federation_username = federator
  federation_password = secret

[shovel.
  source_cluster].
  endpoint = cluster-a.internal
  queue = source-queue
  prefetch_count = 100

Configure Shovel via the management UI or rabbitmqctl:

# Create a shovel from management UI or CLI
rabbitmqctl set_parameter shovel my-shovel \
  '{"src-uri": "amqp://source-cluster", \
    "src-queue": "my-queue", \
    "dest-uri": "amqp://dest-cluster", \
    "dest-queue": "my-queue-replicated"}'

Cluster Federation

Federation aggregates messages from multiple clusters into a single downstream cluster. Where Shovel connects two brokers point-to-point, Federation connects multiple upstream clusters to a downstream cluster automatically.

# rabbitmq.conf - Federation upstream configuration
federation.exchanges.1.exchange = my-exchange
federation.exchanges.1.type = direct
federation.exchanges.1.upstream = upstream-1
federation.exchanges.1.upstream = upstream-2

[upstream upstream-1].
uri = amqp://cluster-1.internal
exchange = my-exchange
prefetch_count = 1000

[upstream upstream-2].
uri = amqp://cluster-2.internal
exchange = my-exchange
prefetch_count = 1000

Federation synchronizes exchanges across clusters. When a message is published to an exchange on any upstream cluster, it becomes available on the federated exchange on the downstream cluster.

When to Use Shovel vs Federation

ScenarioShovelFederation
Point-to-point replicationYesPossible but complex
Multi-source aggregationNoYes
Disaster recovery replicaYesYes
Live migrationYesYes
Geographic distributionNoYes

For most cross-datacenter needs, Federation is easier to manage since it automatically reconnectes and republishes from all upstreams. Shovel is better when you need precise control over which queue maps to which.

Backpressure Handling

Backpressure controls how RabbitMQ delivers messages when consumers cannot keep up.

Backpressure Handling Overview

RabbitMQ uses several complementary mechanisms to handle backpressure when consumers cannot keep up with producers.

Credit-flow mechanism at the protocol level. RabbitMQ uses an AMQP credit system where consumers grant credits to the broker. Each credit represents permission to deliver one message. When a consumer runs low on credits (because it is busy processing), the broker stops sending new messages. This prevents consumers from being overwhelmed by deliveries they cannot handle.

Prefetch (QoS): The basic.qos method caps unacknowledged messages per consumer. A low prefetch (like 1) gives strict fair dispatch but lower throughput. A high prefetch keeps consumers busy but risks message pile-up if a consumer crashes mid-batch. Tuning prefetch is the first line of defense against backpressure.

Queue overflow policies: When a queue hits its max length, RabbitMQ applies the configured overflow behavior. reject-publish blocks the producer (returning a basic.nack). drop-head removes the oldest messages to make room. reject-publish-dlx rejects and dead-letters the new message. These policies give you predictable behavior instead of silent message loss.

Memory and disk alarms act as a last resort. When broker-wide thresholds are exceeded, all producers on the affected node are blocked until resources recover. This protects the broker from crashing but causes a hard pause. Design your producers to handle connection blocks gracefully.

In practice, combine prefetch limits per consumer with queue-level length caps, and monitor queue depth as the canary for developing backpressure.

The prefetch setting limits how many unacknowledged messages RabbitMQ sends to a consumer:

// Limit to 10 unacked messages at a time
channel.prefetch(10);

Without prefetch, RabbitMQ pushes all messages immediately, which can overwhelm slow consumers. With prefetch, RabbitMQ waits for acknowledgment before delivering more.

graph LR
    subgraph "Without Prefetch"
        B1[All messages<br/>pushed immediately]
        C1[Consumer<br/>overwhelmed]
        B1 --> C1
    end
    subgraph "With Prefetch=10"
        B2[Messages<br/>batched 10]
        C2[Consumer<br/>processes]
        A2[Acknowledges]
        B2 --> C2 --> A2 --> B2
    end

Consumer Scaling

When a queue backs up, scale consumers horizontally:

// Kubernetes HPA example for RabbitMQ consumer deployment
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: task-consumer-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: task-consumer
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: External
    external:
      metric:
        name: rabbitmq_queue_messages
        selector:
          matchLabels:
            queue: tasks
      target:
        type: AverageValue
        averageValue: "100"

Queue Overflow Policies

When queues reach max length, configure the overflow behavior:

channel.assertQueue("tasks", {
  durable: true,
  arguments: {
    "x-max-length": 10000, // Max 10000 messages
    "x-overflow": "reject-publish", // Reject new messages (not drop-head)
  },
});

Overflow options:

PolicyBehaviorUse Case
drop-headRemove oldest message to accept new oneWhen old messages are less important
reject-publishReject new message (publisher gets nack)When no message loss is acceptable
reject-publish-dlxRoute rejected messages to DLQDebug overflow scenarios

Slow Consumer Detection

RabbitMQ detects slow consumers and can redistribute messages:

// Consumer reports processing time
channel.consume("tasks", (msg) => {
  const start = Date.now();
  processTask(msg);
  const duration = Date.now() - start;

  // Tag message with processing metadata
  channel.ack(msg, false, {
    processingTimeMs: duration,
  });
});

Monitor consumer lag and alert when average processing time increases beyond thresholds.

Memory and Disk Alarm Thresholds

RabbitMQ blocks producers when memory or disk usage exceeds thresholds. Understanding these limits prevents unexpected pauses.

Memory and Disk Alarm Thresholds Overview

RabbitMQ monitors two critical system resources and automatically blocks producers when either crosses a configured threshold.

Memory alarms are based on the vm_memory_high_watermark setting (default: 40% of available RAM). When RabbitMQ’s memory usage passes this mark, it pauses all publishers on the affected node. Memory is released as consumers acknowledge messages, as messages expire, or when messages are paged to disk. You can set both a relative watermark (percentage of total RAM) and an absolute value if you need more precise control.

Disk alarms protect against full disks that would prevent RabbitMQ from persisting messages. The disk_free_limit defaults to 50MB (absolute) or can be set as a percentage of total disk space. When free space drops below this limit, RabbitMQ blocks producers on that node until space is freed.

SettingDefaultRecommendation
vm_memory_high_watermark.rel0.40.6–0.7 for dedicated nodes
vm_memory_high_watermark.absnoneSet if RAM is shared with other
disk_free_limit.relativenone0.1 (10% free) for production
disk_free_limit.absolute50MB1–5GB for high-volume systems

These alarms happen at the node level in a cluster, not globally. A single node hitting its memory threshold blocks only producers connected to that node. Design your producers with retry logic and connection failover so a blocked node does not cascade into a full outage.

By default, RabbitMQ blocks producers when memory exceeds 40% of available RAM. This is configurable:

# Set memory threshold to 50% of available RAM
vm_memory_high_watermark.relative = 0.5

To calculate the actual threshold on a server with 16 GB RAM:

def calculate_memory_threshold(total_ram_gb, watermark=0.4):
    """
    Calculate RabbitMQ memory alarm threshold.
    """
    # Reserve ~1 GB for OS and Erlang overhead
    reserved_gb = 1.0
    usable_gb = total_ram_gb - reserved_gb

    threshold_gb = usable_gb * watermark

    return {
        'total_ram_gb': total_ram_gb,
        'reserved_os_gb': reserved_gb,
        'usable_gb': usable_gb,
        'threshold_gb': threshold_gb,
        'threshold_pct': watermark * 100
    }

# Example: 16 GB server with 40% default watermark
result = calculate_memory_threshold(16)
# threshold_gb = 6.0 GB

Disk Alarm Threshold

RabbitMQ blocks producers when free disk space falls below a threshold (default: 50 MB). This prevents the broker from running out of disk during a write burst:

# Set disk free space threshold to 2 GB
disk_free_limit.absolute = 2GB

For persistent messages, size your disk threshold based on your ingress rate and how long you want to survive a disk issue:

def calculate_disk_threshold(max_ingress_mbps, survive_minutes=5, safety_factor=1.5):
    """
    Calculate minimum disk threshold based on message ingress rate.
    """
    # Bytes that could arrive during the survival window
    max_arrival_bytes = max_ingress_mbps * 1024 * 1024 * survive_minutes * 60

    # Apply safety factor and convert to GB
    threshold_gb = (max_arrival_bytes / (1024**3)) * safety_factor

    return {
        'max_ingress_mbps': max_ingress_mbps,
        'survive_minutes': survive_minutes,
        'safety_factor': safety_factor,
        'recommended_threshold_gb': round(threshold_gb, 1)
    }

# Example: 100 MB/s ingress rate, survive 5 minutes
result = calculate_disk_threshold(100)
# recommended_threshold_gb = 5.3 GB

Monitoring Alarm Health

def check_alarm_health(rabbitmq_api="http://localhost:15672/api"):
    """
    Check memory and disk alarm status via RabbitMQ HTTP API.
    """
    import requests

    overview = requests.get(f"{rabbitmq_api}/overview").json()
    memory_pct = overview['memory_used'] / overview['memory_limit'] * 100

    # Get disk space
    nodes = requests.get(f"{rabbitmq_api}/nodes").json()
    for node in nodes:
        if node['running']:
            disk_free_mb = node['disk_free'] / (1024 * 1024)
            disk_limit_mb = node['disk_free_limit'] / (1024 * 1024)

    return {
        'memory_used_pct': round(memory_pct, 1),
        'disk_free_mb': round(disk_free_mb, 1),
        'disk_limit_mb': round(disk_limit_mb, 1),
        'memory_alarm': memory_pct >= 95,
        'disk_alarm': disk_free_mb <= disk_limit_mb * 1.5  # Warning at 1.5x
    }

Alert when memory exceeds 70% or disk free space falls below 2x the threshold. These are early warning signs before producers get blocked.

Plugin Ecosystem

RabbitMQ has a plugin ecosystem. These plugins extend functionality for management, monitoring, and protocol support.

Plugin Ecosystem Overview

RabbitMQ ships with a plugin system. You enable plugins with rabbitmq-plugins enable, they ship as .ez archives bundled with the installation, and you get everything from management dashboards to MQTT support to LDAP auth without building anything from source.

The official plugin catalog breaks into a few rough categories. Management & monitoring plugins give you a web UI and Prometheus metrics. Protocol extension plugins let MQTT, STOMP, and AMQP 1.0 clients talk to the same broker alongside AMQP 0-9-1 clients. Auth backends plug in LDAP, OAuth 2.0, or a custom HTTP service instead of RabbitMQ’s internal user database. Data movement plugins — Shovel and Federation — handle cross-cluster replication. There are more, but those are the ones you will actually use.

The subsections below cover the management UI, the Prometheus exporter, and a table of other plugins worth knowing about. A word of caution: only enable the plugins you need. Each one adds memory and CPU overhead, and some open new attack surfaces (the management UI is the most common one).

Management UI

The management plugin provides a web interface for monitoring and administration:

# Enable management plugin
rabbitmq-plugins enable rabbitmq_management

# Access at http://broker:15672/
# Default guest/guest credentials (change in production)

The management UI shows queue depths, message rates, connections, and node health. It also lets you create exchanges, queues, and bindings without the CLI.

Prometheus Exporter

The prometheus plugin exposes metrics in Prometheus format:

# Enable Prometheus plugin
rabbitmq-plugins enable rabbitmq_prometheus

# Metrics available at http://broker:15692/metrics

Key metrics to monitor:

MetricWhat It Tells You
rabbitmq_queue_messagesQueue depth per queue
rabbitmq_connectionsActive connections count
rabbitmq_channel_closedChannel close rate (indicates errors)
rabbitmq_process_resident_memory_bytesMemory usage per node
rabbitmq_disk_space_available_bytesFree disk space
rabbitmq_queue_messages_readyMessages waiting to be consumed

Example Prometheus alert rules:

groups:
  - name: rabbitmq
    rules:
      - alert: RabbitMQMemoryHigh
        expr: rabbitmq_resident_memory_bytes / rabbitmq_memory_limit > 0.8
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "RabbitMQ memory usage above 80%"

      - alert: RabbitMQDiskSpaceLow
        expr: rabbitmq_disk_space_available_bytes < 2 * 1024 * 1024 * 1024
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "RabbitMQ disk space below 2GB"

Other Useful Plugins

PluginPurposeEnable Command
rabbitmq_shovel_managementManage shovels via UIrabbitmq-plugins enable rabbitmq_shovel_management
rabbitmq_federation_managementManage federation via UIrabbitmq-plugins enable rabbitmq_federation_management
rabbitmq_auth_backend_ldapLDAP authenticationrabbitmq-plugins enable rabbitmq_auth_backend_ldap
rabbitmq_mqttMQTT protocol supportrabbitmq-plugins enable rabbitmq_mqtt
rabbitmq_web_stompWebSocket STOMP supportrabbitmq-plugins enable rabbitmq_web_stomp

Spring AMQP Example

For Java enterprise applications, Spring AMQP provides an abstraction over RabbitMQ:

// Configuration
@Configuration
public class RabbitMQConfig {
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory("broker.internal");
        factory.setUsername("app-user");
        factory.setPassword("app-password");
        factory.setVirtualHost("/production");
        return factory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory factory) {
        RabbitTemplate template = new RabbitTemplate(factory);
        template.setMessageConverter(new Jackson2JsonMessageConverter());
        return template;
    }

    @Bean
    public Queue taskQueue() {
        return QueueBuilder.durable("task-queue")
            .withArgument("x-dead-letter-exchange", "dlx")
            .withArgument("x-message-ttl", 86400000)  // 24 hour TTL
            .build();
    }
}

// Producer
@Service
public class TaskProducer {
    private final RabbitTemplate rabbitTemplate;

    public TaskProducer(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    public void submitTask(Task task) {
        rabbitTemplate.convertAndSend("task-queue", task);
    }
}

// Consumer with acknowledgment
@Service
public class TaskConsumer {
    @RabbitListener(queues = "task-queue")
    public void processTask(Task task, Channel channel,
                           @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
        try {
            // Process the task
            doWork(task);

            // Acknowledge on success
            channel.basicAck(tag, false);
        } catch (Exception e) {
            // Reject and don't requeue (sends to DLQ)
            channel.basicNack(tag, false, false);
        }
    }
}

Spring AMQP handles connection pooling, automatic recovery, and message conversion. The @RabbitListener annotation declaratively registers consumers. Use basicAck and basicNack for manual acknowledgment control.

RabbitMQ vs Kafka

RabbitMQ and Kafka solve overlapping problems but work differently:

AspectRabbitMQKafka
ModelBroker routes to queuesDistributed log
RetentionUntil consumed + policyConfigurable retention
OrderingPer queuePer partition
ReplayLimitedFull replay
ThroughputModerate (10k-100k/s)Very high (1M+/s)
Message prioritySupportedNot supported
TransactionsSingle queueMulti-partition atomic

RabbitMQ is better when:

  • You need message priority
  • You want push-based delivery (RabbitMQ pushes, Kafka polls)
  • Your routing logic is complex (multiple exchange types)
  • You do not need replay

Kafka is better when:

  • You need replay of historical data
  • You have very high throughput needs
  • Multiple independent consumers need the same stream
  • You are building event streaming pipelines

For understanding where RabbitMQ fits in messaging patterns, see message queue types and pub/sub patterns.

RabbitMQ Streams

RabbitMQ Streams is a newer queue type introduced as a plugin that provides persistent, replicated message streaming. Unlike traditional queues, streams are designed for high-throughput scenarios where message replay is important.

RabbitMQ Streams Overview

RabbitMQ Streams is a queue type backed by the rabbitmq_stream plugin that uses an append-only log storage model instead of the traditional in-memory queue structure. This adds Kafka-like capabilities to RabbitMQ.

Streams treat messages as an immutable log: consumers read from a specific offset and can re-read old messages as long as they are within the retention window. This is fundamentally different from classic queues, where consuming a message removes it. Retention is time-based (configured with x-max-age) or size-based (x-max-length-bytes), and messages survive until the retention policy clears them.

Key characteristics that set streams apart from classic queues:

  • Offset-based consumption: consumers track their position in the log independently, enabling replay from any point
  • Partitioning: streams can be partitioned for horizontal scaling, with each partition having its own consumer group
  • Server-side filtering: streams support predicate-based filtering so consumers only receive matching messages
  • Batch delivery: messages are delivered in batches for higher throughput, reducing per-message overhead

Streams do not replace classic queues; they address different needs. Use classic queues for task distribution, RPC, and workloads where message removal after consumption is expected. Use streams for event sourcing, audit logs, and scenarios where multiple consumer groups need independent access to the same message history.

Streams offer different semantics compared to standard queues:

FeatureTraditional QueueStream
RetentionUntil consumed + policyConfigurable time-based
ReplayLimited (only DLQ)Full replay from retention
Consumer groupsEach gets full queueIndependent offset per group
OrderingPer queuePer partition
ScalingLimited by queue designHorizontal via partitioning
ThroughputModerateVery high

When to Use Streams

Streams are ideal when you need:

  • Event sourcing: Store complete event history and replay from any point
  • Audit logging: Keep all messages for compliance and debugging
  • Multiple consumers: Different consumer groups access the same stream independently
  • Time-series data: Retain messages for a sliding window (e.g., 7 days)
  • High throughput: Streams are optimized for sequential access patterns
// Declare a stream (requires rabbitmq-stream plugin)
channel.assertQueue("audit-log", {
  durable: true,
  arguments: {
    "x-queue-type": "stream", // This makes it a stream
    "x-max-length-bytes": 1073741824, // 1GB retention
    "x-stream-max-segment-size-bytes": 104857600, // 100MB segments
  },
});

Streams use a different storage mechanism than queues - they append to segment files and support efficient offset-based consumption. Consumer groups maintain their own offset independently, allowing multiple independent consumers on the same stream.

Trade-off Analysis

Understanding RabbitMQ’s design trade-offs helps make better architectural decisions.

Trade-offDescriptionWhen It Matters
Durability vs PerformanceDurable queues + persistent messages are slower but survive broker restartsCritical data vs high-throughput needs
Ordering vs ScalabilitySingle consumer = strict ordering; multiple consumers = better throughputFinancial transactions vs background jobs
Memory vs ThroughputHigher prefetch = more throughput but more memory per consumerSlow consumers vs fast consumers
Queue Limits vs UnboundedMax length prevents memory issues but may drop messagesPredictable workloads vs burst handling
Clustering vs ComplexityHA clusters improve reliability but add operational complexityMission-critical vs development envs
Quorum vs Classic MirroredQuorum = stronger guarantees + higher memory; Classic = lower overheadCritical data vs non-critical data
Exactly-Once vs PerformancePublisher confirms + consumer deduplication adds significant overheadFinancial processing vs logging

Priority Queue Trade-offs

Priority queues in RabbitMQ have specific costs:

AspectClassic QueuePriority Queue
Memory overheadLowHigher (per-message priority)
Routing complexitySimpleMore complex
Message orderingStrict per queueInterleaved by priority
Use case fitGeneral work distributionUrgent task prioritization

Stream vs Queue Resource Trade-offs

ResourceTraditional QueueRabbitMQ Streams
Disk I/ORandom access patternSequential append only
Memory footprintQueue depth + consumersSegment-based, more predictable
Network bandwidthPer-message overheadBatch-oriented, more efficient
Replay capabilityLimited (DLQ only)Full offset-based replay

When to Use / When Not to Use

When to Use RabbitMQ

  • Complex routing requirements: When you need direct, fanout, topic, or headers-based routing
  • Message priority: When some messages must be processed before others (RabbitMQ supports priority queues)
  • Push-based delivery: When you want the broker to push messages to consumers rather than consumers polling
  • Small to medium throughput: When 10k-100k messages per second meets your needs
  • Flexible exchange model: When your routing logic changes frequently or is complex
  • Rapid prototyping: When you need a messaging system quickly without significant operational overhead

When Not to Use RabbitMQ

  • Very high throughput: When you need millions of messages per second (use Kafka)
  • Message replay: When you need to reprocess historical messages (Kafka retains logs)
  • Multiple independent consumers on same stream: When many consumer groups need independent access to the full stream
  • Event sourcing with long retention: When you need months of event history
  • Global topics spanning data centers: When you need geo-replication at the protocol level
  • Simpler queuing needs: When you just need point-to-point without routing flexibility (SQS or a simple broker may suffice)

Production Failure Scenarios

FailureImpactMitigation
Node failureQueues on that node unavailableUse cluster with mirrored queues; Quorum queues for critical data
Network partitionSplit-brain scenarios possibleUse Quorum queues; configure partition handling strategy
Memory pressureRabbitMQ stops accepting messagesMonitor memory; configure memory alarms; set queue limits
Disk pressureRabbitMQ blocks producersMonitor disk space; configure disk free space threshold
Consumer crash mid-processingMessage requeued if not acknowledgedUse manual acknowledgment; implement dead letter queues
Exchange routing failureMessages dropped or routed incorrectlyUse mandatory flag; implement return handlers
Queue overflowMessages reject or evict based on policySet max length policy; configure overflow behavior
Connection failureProducers/consumers disconnectedImplement automatic recovery; use heartbeats

Common Pitfalls / Anti-Patterns

Common Pitfall Patterns

I keep seeing the same mistakes in production RabbitMQ setups, usually right after they have already caused an outage. Most share a common origin: the configuration that works fine in development — auto-ack, unbounded queues, one queue for everything — becomes a time bomb as soon as traffic picks up.

Each of the six pitfalls below hits differently depending on your workload. Auto-ack with idempotency is harmless for a metrics pipeline where a dropped datapoint is noise, but it is a disaster waiting to happen for a payment processor. The hard part is recognising which one applies to your situation before it catches you.

Go through the list, check your own deployments, and close the gaps. The fixes are cheap. The recovery, if you skip them, is not.

Pitfall 1: Auto-Ack Without Idempotency

Auto-ack discards messages on delivery. If the consumer crashes before processing completes, the message is lost. Always use manual acknowledgment and implement idempotent processing.

Pitfall 2: Not Setting Queue Limits

Without max length or message TTL, queues grow unbounded and exhaust memory or disk. Always set appropriate limits.

Pitfall 3: Using a Single Queue for Multiple Message Types

Mixing message types in one queue makes consumers brittle. Use separate queues or exchanges per message type for clarity and isolation.

Pitfall 4: Not Handling Poison Messages

Messages that repeatedly fail processing block the queue. Configure dead letter exchanges and queues to capture these for investigation.

Pitfall 5: Creating Exchanges/Queues at Runtime

Creating exchanges and queues on the fly is expensive and error-prone. Declare them at application startup with proper durability settings.

Pitfall 6: Ignoring Prefetch Settings

Without prefetch limits, RabbitMQ pushes all messages to consumers immediately, overwhelming slow consumers. Set appropriate prefetch (QoS) values.

The Exchange-Queue-Binding Model

RabbitMQ routes messages through a three-tier model:

  1. Producers publish to exchanges
  2. Exchanges route to queues based on rules
  3. Consumers receive from queues
graph LR
    Producer -->|publish| Exchange[Exchange]
    Exchange -->|route| Q1[Queue 1]
    Exchange -->|route| Q2[Queue 2]
    Exchange -->|route| Q3[Queue 3]

Exchange Types

RabbitMQ has four built-in exchange types, each with different routing behavior.

Exchange Types Overview

RabbitMQ offers four built-in exchange types, each with a different routing strategy. The exchange type determines how the broker matches incoming messages to bound queues.

TypeRouting RuleTypical Use Case
DirectExact match on routing keyPoint-to-point, task distribution
FanoutBroadcast to all bound queuesPub/sub, event notifications
TopicWildcard pattern (*, #)Filtered broadcast, log routing
HeadersMatch on message header attributesContent-based routing, multi-attr

Choose Direct when a message should reach a specific queue based on a fixed routing key. Use Fanout when every bound consumer needs every message. Reach for Topic when consumers self-select based on wildcard patterns. Pick Headers when routing depends on message attributes rather than the routing key. The sections below walk through each type with concrete examples.

Fanout Exchange

Routes to all queues bound to the exchange, ignoring binding keys:

Binding: queue1, queue2, queue3 all bound to fanout exchange
Publish: -> all three queues receive a copy

Use this for broadcast scenarios.

Topic Exchange

Routes based on wildcard pattern matching:

Binding: queue1 bound with "orders.*"
Binding: queue2 bound with "orders.created"
Binding: queue3 bound with "orders.#"

Publish: routing key "orders.created" -> queue1, queue2, queue3
Publish: routing key "orders.shipped" -> queue1, queue3
Publish: routing key "users.created" -> no match

The * matches one word, # matches zero or more words.

Headers Exchange

Routes based on message headers instead of routing keys. Less common but useful when routing logic does not fit routing key patterns.

Queue Fundamentals

Queue Fundamentals Overview

Queues are the storage backbone of RabbitMQ. Producers publish to exchanges, which route into queues, and consumers pull from queues. How you configure each queue — its bindings, durability, replication strategy, and dead letter policy — determines the reliability and behavior of your messaging system.

This section covers the building blocks: how binding keys connect queues to exchanges, the queue properties that control durability and lifecycle, the difference between quorum queues and classic mirrored queues for high availability, and how dead letter queues capture failed messages for later inspection.

Binding Keys and Routing Keys

When a queue binds to an exchange, it specifies a binding key (a pattern like orders.* or *.created). When a producer publishes a message, it includes a routing key (like orders.created). The exchange matches the routing key against binding keys to decide which queues get the message.

Queue Properties

  • Durable: Messages survive broker restart (stored on disk)
  • Exclusive: Only one consumer allowed (auto-delete when connection closes)
  • Auto-delete: Queue removed when last consumer unsubscribes
  • Arguments: TTL, max length, dead letter configuration

Quorum Queues vs Classic Mirrored Queues

RabbitMQ offers two queue replication strategies. Classic mirrored queues were the original HA approach. Quorum queues are a newer, more robust alternative based on the Raft consensus algorithm.

graph LR
    subgraph "Classic Mirrored Queue"
        L1[Leader] --> F1[Mirror 1]
        L1 --> F2[Mirror 2]
        Note1[Async replication<br/>Potential data loss on failover]
    end
    subgraph "Quorum Queue"
        QL1[Leader] --> QF1[Follower 1]
        QL1 --> QF2[Follower 2]
        QF1 --> QF2
        QF2 --> QF1
        Note2[Synchronous replication<br/>Zero data loss<br/>Raft-based consensus]
    end
FeatureClassic MirroredQuorum Queue
ReplicationAsync by defaultSynchronous (Raft)
Data loss on failoverPossibleNone
Ordering guaranteePer-queuePer-message
Memory footprintLowerHigher (state machine)
Node failure handlingPromote mirror to leaderRaft leader election
Queue typeClassic onlyStream-compatible
Use caseLegacy HACritical data

For critical messages that cannot be lost, use quorum queues. They provide stronger durability guarantees at the cost of higher resource usage.

Dead Letter Queues

When a message cannot be delivered (no matching queue), it is dead lettered. More usefully, when a consumer rejects a message or it times out, it can go to a dead letter queue (DLQ) for later inspection:

Important preconditions for dead lettering: Messages can only be dead-lettered if they are explicitly rejected by a consumer with requeue=false (using basicNack), or if their TTL expires. If a consumer rejects a message with requeue=true, the message is requeued to the original queue and will not be dead-lettered.

graph LR
    Pub[Publisher] --> Exchange
    Exchange -->|route| Queue[Main Queue]
    Queue -->|reject/timeout| DLX[Dead Letter Exchange]
    DLX -->|route| DLQ[Dead Letter Queue]

This is essential for debugging poison messages that crash consumers repeatedly.

Consumer Patterns

Consumer Patterns Overview

How consumers connect to queues and process messages directly affects throughput, reliability, and resource usage. RabbitMQ supports several consumer patterns suited to different workloads and scaling needs.

This section covers three foundational topics. The direct consumer pattern — one consumer pulling from one queue — is the simplest starting point. Competing consumers scale horizontally by placing multiple consumers on the same queue with round-robin message distribution. Acknowledgment modes control whether messages are marked as processed immediately on delivery or only after the consumer has finished working. Each pattern balances trade-offs between throughput, ordering guarantees, and operational complexity.

Direct Consumer

The simplest pattern: one consumer per queue:

channel.consume("tasks", (msg) => {
  const task = JSON.parse(msg.content.toString());
  processTask(task);
  channel.ack(msg);
});

Competing Consumers

Multiple consumers on the same queue. RabbitMQ round-robins messages:

// Consumer 1
channel.consume("tasks", (msg) => {
  processTask(msg);
  channel.ack(msg);
});

// Consumer 2 (separate process or connection)
channel.consume("tasks", (msg) => {
  processTask(msg);
  channel.ack(msg);
});

Messages are distributed evenly. If one consumer is slow, it gets fewer messages over time as RabbitMQ detects the backpressure.

Acknowledgment Modes

  • Auto-ack: Message considered processed immediately on delivery (dangerous - lost if consumer crashes)
  • Manual ack: Consumer explicitly acknowledges after successful processing
channel.consume("tasks", (msg) => {
  const task = JSON.parse(msg.content.toString());
  try {
    processTask(task);
    channel.ack(msg);
  } catch (e) {
    // requeue or send to DLQ
    channel.nack(msg, false, false); // don't requeue
  }
});

Delivery Semantics

Delivery Semantics Overview

Delivery semantics define the guarantees a messaging system provides about whether and how often a message is processed. RabbitMQ supports three levels, each controlled by how you configure acknowledgments, publisher confirms, and consumer-side deduplication.

  • At-most-once: Messages are acknowledged immediately on delivery. Fastest path, but offers no protection against consumer crashes. Best for idempotent, non-critical data where occasional loss is acceptable.
  • At-least-once: Consumers acknowledge only after processing succeeds. If the consumer crashes before acknowledging, the message is re-delivered to another consumer. This is the default for most production workloads.
  • Exactly-once: Combines publisher confirms with consumer-side deduplication tracking. Eliminates duplicates at the cost of additional complexity and overhead.

The right choice depends on your tolerance for duplicate processing versus your tolerance for message loss. The sections below show code for each pattern, from simplest to most robust.

At-Most-Once Delivery

With auto-ack enabled, messages are acknowledged before processing:

channel.consume(
  "tasks",
  (msg) => {
    // Message considered delivered immediately
    // If consumer crashes here, message is lost
    processTask(msg.content.toString());
  },
  { noAck: true },
);

This is the fastest mode but offers no reliability. Use only for idempotent, non-critical messages.

At-Least-Once Delivery

With manual acknowledgment after successful processing:

channel.consume("tasks", (msg) => {
  const task = JSON.parse(msg.content.toString());
  try {
    processTask(task);
    channel.ack(msg); // Ack after successful processing
  } catch (e) {
    channel.nack(msg, false, true); // Requeue on failure
  }
});

The message is requeued if processing fails. You may see duplicates if the consumer crashes after processing but before acknowledgment. Your consumers must handle duplicates idempotently.

Exactly-Once Delivery

RabbitMQ supports publisher confirms combined with consumer acknowledgments:

// Publisher side - enable confirms
channel.confirmSelect();

// Publish with confirm
channel.publish("", "my-queue", content, { persistent: true }, (err) => {
  if (!err) {
    // Message persisted to disk and replicated
  }
});

On the consumer side, track processed message IDs in a database or cache to detect and discard duplicates:

const processed = new Set();

channel.consume("tasks", (msg) => {
  const taskId = msg.properties.messageId;
  if (processed.has(taskId)) {
    channel.ack(msg); // Already processed, just ack
    return;
  }

  processTask(msg);
  processed.add(taskId);
  channel.ack(msg);
});

Exactly-once is expensive and complex. Only use it when duplicate processing is unacceptable and the overhead is acceptable.

Interview Questions

Common interview questions for RabbitMQ roles:

1. Explain the difference between direct, fanout, topic, and headers exchange types in RabbitMQ.

Expected answer points:

  • Direct: Routes messages to queues where binding key exactly matches routing key. Use for point-to-point communication.
  • Fanout: Routes to all queues bound to the exchange, ignoring binding keys. Use for broadcast scenarios.
  • Topic: Routes based on wildcard pattern matching with * (one word) and # (zero or more words). Use for complex routing rules.
  • Headers: Routes based on message headers instead of routing keys. Use when routing logic does not fit routing key patterns.
2. What is a dead letter queue and when would you use one?

Expected answer points:

  • A dead letter queue captures messages that cannot be delivered or processed successfully
  • Messages enter the DLQ when consumers reject them, when they timeout, or when no matching queue exists
  • Use DLQs to inspect poison messages that crash consumers repeatedly
  • Configure via x-dead-letter-exchange and x-dead-letter-routing-key arguments on queues
  • Essential for debugging and maintaining system health without losing failed message data
3. What is the difference between quorum queues and classic mirrored queues?

Expected answer points:

  • Classic mirrored queues: Original HA approach with async replication. A leader queue on one node mirrors to follower queues on other nodes. Potential data loss on failover.
  • Quorum queues: Newer alternative based on Raft consensus algorithm. Synchronous replication ensures zero data loss. Higher memory footprint due to state machine replication.
  • Recommendation: Use quorum queues for critical messages that cannot be lost; classic mirrored for legacy systems or non-critical data
4. Describe the three delivery semantics RabbitMQ supports and when each applies.

Expected answer points:

  • At-most-once: Auto-ack before processing. Fastest but messages can be lost if consumer crashes. Use for idempotent, non-critical messages.
  • At-least-once: Manual ack after successful processing. Messages are requeued on failure. May see duplicates. Consumers must handle duplicates idempotently.
  • Exactly-once: Publisher confirms combined with consumer deduplication. Most expensive and complex. Use when duplicate processing is unacceptable.
5. How does prefetch (QoS) work in RabbitMQ and why is it important?

Expected answer points:

  • Prefetch limits unacknowledged messages delivered to a consumer at any time
  • Without prefetch, RabbitMQ pushes all messages immediately, potentially overwhelming slow consumers
  • With prefetch, RabbitMQ waits for acknowledgment before delivering more messages
  • Tune prefetch based on consumer processing speed and memory constraints
  • Prefetch=1 gives strict sequential processing; higher values improve throughput with parallelism
6. What is the difference between Shovel and Federation plugins?

Expected answer points:

  • Shovel: Point-to-point message movement between specific brokers. Connect to source, consume messages, publish to destination. Good for disaster recovery and precise queue-to-queue mapping.
  • Federation: Multi-source aggregation from multiple upstream clusters to a downstream cluster. Automatically reconnectes and republishes from all upstreams. Better for geographic distribution.
  • Use Shovel when you need precise control over replication topology
  • Use Federation when you need automatic multi-source aggregation
7. How does RabbitMQ handle backpressure when consumers cannot keep up with producers?

Expected answer points:

  • Memory/disk alarms block producers when thresholds are exceeded
  • Queue overflow policies: drop-head removes old messages, reject-publish nacks new messages
  • Prefetch limits prevent consumer overwhelm by batching deliveries
  • Horizontal scaling of consumers redistributes load via competing consumers pattern
  • Monitor consumer lag and set up alerts for queue depth growth
8. When would you choose RabbitMQ over Kafka?

Expected answer points:

  • Choose RabbitMQ when you need message priority support
  • Choose RabbitMQ when you want push-based delivery (broker pushes to consumers)
  • Choose RabbitMQ when routing logic is complex (multiple exchange types)
  • Choose RabbitMQ for small to medium throughput (10k-100k/s)
  • Choose RabbitMQ for flexible routing that changes frequently
  • Choose Kafka when you need replay of historical data or very high throughput (1M+/s)
9. What are the key metrics to monitor in a production RabbitMQ deployment?

Expected answer points:

  • Queue depth: messages waiting in each queue
  • Message rate: published, delivered, acknowledged per second
  • Consumer count: active consumers per queue
  • Connection and channel counts
  • Memory and disk usage relative to configured alarms
  • Unacked messages (indicates consumer lag or failures)
  • Message rejection rate and dead letter queue depth
10. How would you implement exactly-once delivery in RabbitMQ?

Expected answer points:

  • Publisher side: Enable confirms with channel.confirmSelect() and wait for acknowledgment callbacks
  • Ensure persistent messages with deliveryMode: 2 and durable queues
  • Consumer side: Generate unique message IDs, track processed IDs in a database or cache
  • On consume: Check if message ID exists in processed set; if yes, ack without reprocessing
  • Combine publisher confirms + manual consumer acks + deduplication tracking
  • Accept the performance overhead; only use when duplicate processing is truly unacceptable
11. How does RabbitMQ handle message ordering and what are the limitations?

Expected answer points:

  • RabbitMQ provides per-queue ordering guarantees - messages are delivered in FIFO order within a single queue
  • With single active consumer, ordering is strictly preserved
  • With multiple consumers (competing consumers), messages are distributed round-robin, so consumption order across consumers is not guaranteed
  • Quorum queues maintain per-message ordering rather than per-queue
  • Cross-queue ordering is not supported - messages in different queues have no ordering relationship
  • Use single consumer per queue if strict ordering is required
12. What are virtual hosts (VHOSTs) in RabbitMQ and why are they useful?

Expected answer points:

  • VHOSTs are isolated namespaces within a single RabbitMQ broker
  • Each VHOST has its own exchanges, queues, bindings, and permissions
  • Users can have different permissions per VHOST (read, write, configure)
  • VHOSTs enable multi-tenancy - different applications or environments can share a broker while remaining isolated
  • Common pattern: separate VHOSTs for dev, staging, production
  • VHOSTs prevent naming collisions between independent applications
13. Explain the difference between channels and connections in RabbitMQ.

Expected answer points:

  • A connection is a TCP socket between client and broker - expensive to create, handles authentication and heartbeats
  • A channel is a virtual connection within a physical connection - lightweight, multiplexed over a single TCP connection
  • Multiple channels share one TCP connection, reducing network overhead and OS resources
  • Channels are not thread-safe - each thread should use its own channel or implement synchronization
  • Best practice: use a connection pool with multiple channels for concurrent publishing
  • Creating thousands of connections is problematic; creating thousands of channels on one connection is efficient
14. How does memory high watermark work and what happens when it is triggered?

Expected answer points:

  • Memory high watermark is a threshold (default 40% of OS memory) that triggers blocking of publishers
  • When threshold is exceeded, RabbitMQ blocks connections that are in blocking state (publishers wait)
  • Blocked connections stop receiving new messages until memory drops below the threshold
  • Memory is released when messages are consumed, acknowledged, or expire
  • Configure via vm_memory_high_watermark.relative in rabbitmq.conf
  • Critical for preventing out-of-memory conditions on the broker node
15. What is the purpose of the rabbitmqctl management command and when would you use it?

Expected answer points:

  • rabbitmqctl is the CLI tool for RabbitMQ administration and management
  • Common uses: list queues, bindings, exchanges, connections, channels
  • User management: create users, set permissions, list users
  • VHOST management: create/delete VHOSTs, set permissions
  • Queue operations: purge messages, move messages between queues
  • Cluster management: join cluster, leave cluster, forget node
  • Plugin management: enable/disable plugins
  • Reset the broker: rabbitmqctl stop_app && rabbitmqctl reset && rabbitmqctl start_app
16. What are the implications of using auto-delete queues?

Expected answer points:

  • Auto-delete queues are removed when the last consumer unsubscribes
  • Useful for temporary reply queues in RPC patterns
  • Risk: if all consumers disconnect unexpectedly, the queue and unconsumed messages are lost
  • Not suitable for persistent workloads where message durability is critical
  • Often used with exclusive queues in request-reply patterns
  • Consider the lifecycle of producers and consumers before using auto-delete
17. How does RabbitMQ handle network partitions and what are the recovery options?

Expected answer points:

  • Network partitions occur when cluster nodes cannot communicate - split-brain scenarios possible
  • RabbitMQ has three partition handling strategies: pause_minority, autoheal, ignore
  • pause_minority: nodes that detect they are in minority pause until partition heals
  • autoheal: broker chooses a winning partition and restarts nodes in other partitions
  • ignore (default): partitions are not handled automatically - manual intervention required
  • Quorum queues handle partitions better due to Raft consensus
  • Monitor for partition events and test recovery procedures in staging
18. What is the difference between durable and transient (non-durable) queues?

Expected answer points:

  • Durable queues survive broker restarts - definitions stored on disk
  • Transient queues are deleted on broker restart - definitions stored only in memory
  • For durable queues, messages also need to be persistent (deliveryMode=2) to survive restart
  • Durable queues have higher overhead due to disk writes
  • Use durable queues for critical messages that must not be lost
  • Use transient queues for non-critical, temporary data that can be regenerated
19. How would you diagnose and troubleshoot high memory usage in RabbitMQ?

Expected answer points:

  • Check the management UI or API for memory usage per node and process
  • Identify queues with high message counts or large message sizes
  • Check for queues with messages that have long TTL but no consumers (messages accumulating)
  • Review unacked messages - consumers may be slow or crashed
  • Monitor connection and channel counts - too many can increase overhead
  • Use rabbitmq-diagnostics -s memory_breakdown for detailed breakdown
  • Consider increasing memory watermark or adding more RAM if legitimate growth
  • Set queue limits (max-length, max-bytes) to prevent unbounded growth
20. What are the advantages of using message properties and headers in RabbitMQ?

Expected answer points:

  • Message properties include: deliveryMode, contentType, messageId, correlationId, timestamp, expiration, priority
  • Headers exchange uses custom headers for routing instead of routing keys
  • CorrelationId links request-reply pairs in RPC patterns
  • MessageId enables deduplication and exactly-once delivery tracking
  • Priority field supports priority queues for preferential message processing
  • Expiration (TTL) can be set per-message or per-queue
  • Headers are more flexible than routing keys for complex routing logic
  • Useful for content-based routing where routing depends on multiple attributes

Further Reading

Pre-Deployment Checklist

Pre-Deployment Checklist Overview

Run through this checklist before going live. It covers the configuration, security, and operational items that trip up teams in staging and production. Skipping a few here is how you end up with message loss, security gaps, or operational issues that take days to untangle.

- [ ] Queues declared as durable for message persistence
- [ ] Manual acknowledgment implemented in consumers
- [ ] Dead letter exchange and queue configured for failed messages
- [ ] Queue max length and message TTL set appropriately
- [ ] Prefetch (QoS) limit configured for consumer backpressure
- [ ] TLS/SSL enabled for all client connections
- [ ] User permissions scoped to minimum required VHOSTs and resources
- [ ] Memory and disk alarm thresholds configured
- [ ] Clustering configured with quorum queues for critical data
- [ ] Monitoring for queue depth and consumer lag configured
- [ ] Alert thresholds set for memory, disk, and queue depth
- [ ] Consumer connection recovery implemented
- [ ] Message schema validation in place
- [ ] Backup strategy for RabbitMQ configuration documented

Observability and Operations

Observability and Operations Overview

Running RabbitMQ in production requires visibility into broker health, message flow, and resource consumption. Without proper observability, issues like queue buildup, memory pressure, and consumer failures go unnoticed until they cause outages.

This section covers the key metrics to track, the logs to capture for debugging and auditing, and the alerts you should configure to stay ahead of problems. Use the management plugin UI or the HTTP API for real-time visibility, and forward metrics to a monitoring system like Prometheus with the rabbitmq_prometheus plugin.

Metrics to Monitor

  • Queue depth: Messages waiting in each queue
  • Consumer count: Active consumers per queue
  • Message rate: Messages published, delivered, and acknowledged per second
  • Channel count: Open channels per connection
  • Connection count: Active client connections
  • Memory usage: RabbitMQ memory consumption
  • Disk usage: Available disk space for persistence
  • Unacked messages: Messages delivered but not acknowledged

Logs to Capture

  • Queue creation and deletion events
  • Consumer connection and disconnection events
  • Message rejections and dead lettering
  • Queue overflow events
  • Memory and disk alarms
  • Clustering events (node join/leave)

Conclusion

Summary and Key Takeaways

RabbitMQ’s exchange-queue-binding model gives it unmatched routing flexibility among message brokers. The key ideas to carry forward:

  • Exchanges are the routing brain — Direct, Fanout, Topic, and Headers each serve a distinct routing pattern
  • Queues are durable storage — configure TTL, max length, and dead letter policies to prevent unbounded growth
  • Acknowledgment is your safety net — manual acks give you control; auto-ack is only safe for idempotent, disposable messages
  • Quorum queues for critical data — Raft-based replication eliminates data loss at the cost of higher memory
  • Streams add Kafka-like replay — use streams for event sourcing, audit logs, and high-throughput scenarios that need offset-based consumption

The following subsections organize the key points, recommended alerts, and security checklist into actionable lists.

Key Points

  • RabbitMQ uses exchanges to route messages to queues based on binding keys
  • Four exchange types: direct (exact match), fanout (all queues), topic (wildcards), headers (attribute-based)
  • Queues can be durable (persist across restarts) and have TTL, max length, and dead letter policies
  • Manual acknowledgment gives you control over delivery guarantees
  • Dead letter queues capture failed messages for debugging
  • Competing consumers enable horizontal scaling with round-robin distribution
  • Quorum queues provide better replication and consistency than classic mirrored queues

Alerts to Configure

  • Queue depth exceeds threshold
  • Memory usage exceeds 70% of limit
  • Disk free space below threshold
  • Consumer count drops to zero (for critical queues)
  • High message rejection rate
  • Connection count approaching file descriptor limit
  • Cluster node unavailable

Security Checklist

  • Authentication: Use SASL authentication (PLAIN, SCRAM-SHA-256); disable guest/admin defaults
  • Authorization: Implement VHOST and resource-level permissions; principle of least privilege
  • Encryption in transit: Enable SSL/TLS for all connections
  • Encryption at rest: Enable Lore Weaver or use encrypted filesystems for message persistence
  • Connection limits: Set max connections per user and per VHOST
  • Protocol-level security: Disable legacy protocols (SSLv3, TLS 1.0) if not needed
  • Audit logging: Enable RabbitMQ auditing plugin for administrative actions
  • Network segmentation: Place RabbitMQ in private networks; use firewalls

Category

Related Posts

Exactly-Once Delivery: The Elusive Guarantee

Explore exactly-once semantics in distributed messaging - why it's hard, how Kafka and SQS approach it, and practical patterns for deduplication.

#distributed-systems #messaging #kafka

Ordering Guarantees in Distributed Messaging

Understand how message brokers provide ordering guarantees, from FIFO queues to causal ordering across partitions, and the trade-offs in distributed systems.

#distributed-systems #messaging #kafka

Event-Driven Architecture: Events, Commands, and Patterns

Learn event-driven architecture fundamentals: event sourcing, CQRS, event correlation, choreography vs orchestration, and implementation patterns.

#architecture #events #messaging