The Complete RabbitMQ Guide — Features, Patterns & Best Practices
AMQP fundamentals, exchange types, reliability mechanisms, clustering, and messaging patterns.
A deep, practitioner-level reference covering AMQP fundamentals, exchange types, reliability mechanisms, clustering/HA, messaging patterns, performance tuning, and production best practices.
Table of Contents
- Core Concepts
- Exchange Types
- Queues: Types & Properties
- Message Properties & Delivery Semantics
- Publisher Confirms & Reliable Publishing
- Consumer Acknowledgments & Prefetch (QoS)
- Dead Letter Exchanges (DLX)
- TTL: Message & Queue
- Delayed Messages
- Priority Queues
- Clustering & High Availability
- Federation & Shovel
- Security
- Management, Monitoring & Observability
- Messaging Patterns
- Connection & Channel Management Best Practices
- Idempotency & Error Handling
- Performance Tuning
- Common Pitfalls
- Quick Reference Cheat Sheet
1. Core Concepts
RabbitMQ implements AMQP 0-9-1 (with plugins for MQTT, STOMP, and AMQP 1.0). The core abstractions:
- Producer — publishes messages to an Exchange (never directly to a queue).
- Exchange — routes messages to one or more queues based on rules (bindings + routing keys).
- Binding — a link between an exchange and a queue, optionally with a routing key or arguments.
- Queue — an ordered buffer that stores messages until consumed.
- Consumer — subscribes to a queue and processes messages.
- Virtual Host (vhost) — a logical namespace isolating exchanges, queues, and permissions (multi-tenancy).
- Connection — a TCP connection to the broker (expensive to create; long-lived).
- Channel — a lightweight virtual connection multiplexed over a single TCP connection (cheap; one per thread/task).
Golden rule: Producers never publish “to a queue.” They always publish to an exchange with a routing key. The exchange decides where the message goes.
Producer → Exchange → (Binding + Routing Key) → Queue → Consumer
2. Exchange Types
2.1 Direct Exchange
Routes messages to queues whose binding key exactly matches the message’s routing key.
Exchange: orders.direct
Binding: routing_key = "order.created" → Queue: orders.created.q
Binding: routing_key = "order.cancelled" → Queue: orders.cancelled.q
Use case: Task routing where the target is deterministic (e.g., routing by event type).
2.2 Fanout Exchange
Broadcasts to all bound queues, ignoring the routing key entirely.
Use case: Pub/Sub — notifications, cache invalidation, broadcasting events to multiple independent services.
Exchange: notifications.fanout
→ Queue: email.service.q
→ Queue: sms.service.q
→ Queue: audit.log.q
2.3 Topic Exchange
Routes based on pattern matching on the routing key using wildcards:
*matches exactly one word#matches zero or more words
Routing key format: <region>.<severity>.<service>
Binding: "eu.*.payments" matches "eu.error.payments"
Binding: "*.critical.#" matches "us.critical.payments.timeout"
Binding: "eu.#" matches everything starting with "eu."
Use case: Flexible event routing — logging systems, multi-dimensional filtering.
2.4 Headers Exchange
Routes based on message header attributes instead of the routing key. Uses x-match argument:
x-match: all— all headers must match (AND)x-match: any— at least one header must match (OR)
{
"x-match": "all",
"format": "pdf",
"type": "report"
}
Use case: Rarely used in practice — routing key patterns (topic) usually suffice and are faster. Use headers exchange only when routing criteria don’t fit naturally into a string key.
2.5 Default (Nameless) Exchange
A special direct exchange ("") that every queue is automatically bound to, using the queue name as the routing key. Publishing with routing_key = "my_queue" to "" delivers directly to my_queue. Convenient for simple point-to-point, but avoid over-relying on it in larger systems — explicit exchanges make routing topology visible and evolvable.
2.6 Alternate Exchange (AE)
An exchange configured to receive messages that couldn’t be routed anywhere (no matching binding). Prevents silent message loss.
rabbitmqctl set_policy AE-orders "^orders\." '{"alternate-exchange":"orders.unrouted"}' --apply-to exchanges
3. Queues: Types & Properties
3.1 Classic Queues
The original queue type. Single-node data structure, replicated via the deprecated classic mirrored queues (mirrored via ha-mode policy). Being phased out for HA in favor of quorum queues.
3.2 Quorum Queues (recommended for most durable workloads since RabbitMQ 3.8+)
Built on the Raft consensus algorithm. Data-safety-first design:
- Always replicated across multiple nodes (minimum recommended: 3, odd number)
- No “flapping” mirror re-sync issues that classic mirrored queues suffered from
- Better throughput and predictable failover
- Does not support: message priority (pre-3.13), per-message TTL in early versions (now supported), exclusive queues, non-durable queues
# Declare a quorum queue via arguments
channel.queue_declare(queue='orders.q', durable=True, arguments={'x-queue-type': 'quorum'})
3.3 Streams (RabbitMQ 3.9+)
Append-only log abstraction (similar to Kafka topics). Supports:
- Non-destructive reads (multiple independent consumers replaying from any offset)
- High throughput for fan-out-heavy workloads
- Long retention of messages
arguments={'x-queue-type': 'stream', 'x-max-length-bytes': 20_000_000_000}
Choose Streams when: you need replay, large fan-out, or event-sourcing-like semantics. Choose Quorum Queues when: you need traditional work-queue semantics with strong safety guarantees. Avoid Classic Queues for new systems unless you specifically need transient/exclusive queue semantics or per-message priority pre-3.13.
3.4 Exclusive & Auto-Delete Queues
- Exclusive: used by only one connection, deleted when that connection closes. Useful for RPC reply queues or client-specific temp queues.
- Auto-delete: deleted once the last consumer unsubscribes.
3.5 Queue Length Limits & Overflow Behavior
arguments={
'x-max-length': 100000,
'x-max-length-bytes': 500_000_000,
'x-overflow': 'reject-publish' # or 'drop-head' (default)
}
reject-publish is safer for critical data — it nacks new publishes instead of silently dropping the oldest messages.
4. Message Properties & Delivery Semantics
Key AMQP message properties to always set intentionally:
| Property | Purpose |
|---|---|
delivery_mode | 2 = persistent (survives broker restart when queue is durable), 1 = transient |
content_type | e.g. application/json — helps consumers deserialize correctly |
message_id | Unique ID for dedup/tracing |
correlation_id | Ties a request to a response (essential for RPC) |
reply_to | Queue name the responder should publish the answer to |
timestamp | When the message was created |
expiration | Per-message TTL in ms (as a string) |
headers | Arbitrary key-value metadata (tracing, versioning, routing) |
app_id / type | Useful for debugging/observability |
RabbitMQ’s delivery guarantee is “at-least-once” by default (with acks) — never “exactly-once” out of the box. Design consumers to be idempotent (see §17).
5. Publisher Confirms & Reliable Publishing
Without confirms, a publish can silently fail (network blip, broker crash before persisting) and you’d never know.
Publisher Confirms (confirm.select) make the broker asynchronously ack each published message once it’s safely handled (persisted to disk for durable queues, or routed for others).
channel.confirm_delivery() # pika sync helper
# Async pattern (recommended for throughput):
channel.confirm_select()
outstanding = {}
def on_ack(frame):
outstanding.pop(frame.delivery_tag, None)
def on_nack(frame):
# republish or alert — broker couldn't handle the message
handle_failed_publish(frame.delivery_tag)
channel.add_on_return_callback(on_returned) # for mandatory=True unroutable messages
Best practice pattern:
- Enable confirms once per channel.
- Track outstanding (unconfirmed) messages in a map keyed by
delivery_tag. - Publish with
mandatory=Trueif you need to detect unroutable messages (paired with abasic.returnhandler), or use an Alternate Exchange instead (more scalable than per-messagemandatoryflag under high throughput). - Batch confirms for throughput — don’t wait for each message’s ack individually; publish many, then track acks asynchronously.
- On nack or timeout, retry with backoff or dead-letter to an “unconfirmed” audit queue.
Transactions vs Confirms
AMQP transactions (tx.select, tx.commit) exist but are much slower (synchronous round-trip per transaction) — publisher confirms are the modern, performant alternative and should almost always be preferred.
6. Consumer Acknowledgments & Prefetch (QoS)
6.1 Ack Modes
- Manual ack (
auto_ack=False, default recommended): consumer explicitly callsbasic.ackafter successfully processing. If the consumer dies before acking, the message is requeued and redelivered. - Auto ack (
auto_ack=True): broker considers the message delivered the instant it’s sent over the wire — dangerous, messages are lost if the consumer crashes mid-processing. Only use for truly disposable/low-value data. - Nack/Reject:
basic.nack(requeue=True/False)— explicitly signal failure.requeue=Falsetypically routes to a DLX if configured, otherwise discards.
6.2 Prefetch (QoS)
basic.qos(prefetch_count=N) limits how many unacknowledged messages a consumer can hold at once. This is the single most important tuning knob for consumer throughput and fairness.
channel.basic_qos(prefetch_count=50)
- Too low (e.g. 1): safe/fair but throughput-limited — the consumer idles waiting for the next message’s round trip.
- Too high: one slow consumer can hoard thousands of messages while others starve; also risks large redelivery storms if that consumer crashes.
- Rule of thumb: start with prefetch = (desired in-flight messages per consumer) based on processing time × target throughput. For fast, uniform tasks, 100–300 is common. For slow/heavy tasks (seconds each), keep it low (1–10) for fairness across consumers.
- With multiple consumers on one channel, prefetch is shared across them unless set globally=False (per-consumer) — always use per-consumer QoS unless you explicitly want channel-wide sharing.
6.3 Manual Ack Ordering
Acks must be sent in order relative to delivery on a channel; you can batch-ack up to a given delivery tag with multiple=True — but be careful: this acks everything up to and including that tag, so never do this from concurrent workers sharing a channel without coordination.
7. Dead Letter Exchanges (DLX)
Messages become “dead” when:
- Rejected with
requeue=False(basic.nack/basic.reject) - TTL expires
- Queue length limit exceeded (overflow reject)
Configure a queue to route dead messages to a DLX:
channel.queue_declare(
queue='orders.q',
durable=True,
arguments={
'x-dead-letter-exchange': 'orders.dlx',
'x-dead-letter-routing-key': 'orders.failed'
}
)
Standard pattern — Retry with backoff using DLX + TTL:
orders.q --(reject/expire)--> orders.retry.dlx
|
v
orders.retry.q (TTL=5000ms, no consumers,
DLX points back to orders.q)
|
(after TTL expires, message dead-letters back)
v
orders.q (retried)
This “parking lot queue” pattern implements delayed retry without needing a plugin. Attach a header-based retry counter (x-death array, automatically added by RabbitMQ) and inspect x-death[0].count to cap retries and route to a final poison-message / DLQ (dead-letter queue) for manual inspection after N attempts.
def handle_message(ch, method, properties, body):
headers = properties.headers or {}
deaths = headers.get('x-death', [])
retry_count = deaths[0]['count'] if deaths else 0
if retry_count >= 5:
ch.basic_publish(exchange='orders.poison', routing_key='', body=body)
ch.basic_ack(method.delivery_tag)
return
# ... process, nack with requeue=False to trigger retry via DLX
Always configure a DLX for production queues. A queue without a DLX silently loses rejected/expired messages.
8. TTL: Message & Queue
Per-message TTL:
properties = pika.BasicProperties(expiration='60000') # 60 seconds, as a string in ms
Per-queue TTL (applies to all messages in the queue):
arguments={'x-message-ttl': 60000}
Queue TTL (auto-delete an idle queue):
arguments={'x-expires': 1800000} # delete queue after 30 min of no use
Note: if both per-message and per-queue TTL are set, the lower value wins for each message. Also note that TTL expiry is evaluated lazily at the head of the queue in classic queues — a long-lived message stuck behind a not-yet-expired one won’t be dead-lettered until it reaches the head (classic queue behavior differs slightly from quorum/stream).
9. Delayed Messages
RabbitMQ has no native “delay X minutes then deliver” primitive out of the box. Two approaches:
9.1 Delayed Message Exchange Plugin (community plugin)
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
channel.exchange_declare(
exchange='delayed.exchange',
exchange_type='x-delayed-message',
arguments={'x-delayed-type': 'direct'}
)
properties = pika.BasicProperties(headers={'x-delay': 15000}) # 15s delay
Simple to use, but internally stores delayed messages in Mnesia which doesn’t scale well to very high volumes or very long delays — fine for moderate use.
9.2 TTL + DLX “Parking Lot” Pattern (native, more scalable)
Same mechanism as the retry pattern in §7: publish to a queue with x-message-ttl and no consumers, dead-lettering back to the real destination once the TTL expires. Preferred for high-scale or long-delay scenarios since it uses only core primitives.
10. Priority Queues
channel.queue_declare(queue='tasks.q', arguments={'x-max-priority': 10})
properties = pika.BasicProperties(priority=8)
- Max priority levels 1–255 supported, but keep it small (e.g., 0–10) — each priority level adds internal overhead.
- Only classic queues fully supported priority historically; quorum queues gained priority support in later versions — check your RabbitMQ version’s release notes before relying on this with quorum queues.
- Priority only takes effect when there’s a backlog — if consumers keep up in real time, priority rarely matters since messages are consumed almost as fast as they arrive.
11. Clustering & High Availability
11.1 Cluster Basics
- A RabbitMQ cluster shares users, vhosts, permissions, exchanges, and queue metadata across all nodes.
- Queue contents (messages) live on specific node(s) unless using quorum queues/streams (which replicate data itself via Raft).
- Nodes communicate via Erlang distribution — requires reliable low-latency network (same DC/AZ ideally, not WAN).
11.2 Quorum Queues for HA (Recommended)
- Replicate across N nodes using Raft; tolerates
(N-1)/2node failures. - A 3-node quorum queue survives 1 node failure; 5-node survives 2.
- Automatic leader election on failure — clients transparently reconnect to the new leader.
- Configure via policy or declare-time argument:
rabbitmqctl set_policy ha-orders "^orders\." '{"x-queue-type":"quorum"}' --apply-to queues
11.3 Classic Mirrored Queues (Legacy — avoid for new deployments)
Controlled via ha-mode policies (all, exactly, nodes). Known for split-brain/resync pain during network partitions; deprecated path — RabbitMQ team recommends quorum queues instead.
11.4 Load Balancing Clients
Put a TCP load balancer (HAProxy, cloud LB) or client-side node list in front of the cluster. Clients should implement reconnect-with-backoff logic since any node can fail.
11.5 Network Partitions
Configure cluster_partition_handling:
ignore(default, risky)autoheal— cluster picks a winning partition and restarts nodes on the losing sidepause_minority— nodes in the minority partition pause themselves (safer for consistency, common in production)
11.6 Federation vs Clustering
Clustering = tight coupling, same logical broker, low-latency LAN. For cross-datacenter/WAN replication, use Federation or Shovel instead (§12) — never cluster across WAN links.
12. Federation & Shovel
12.1 Federation
Links exchanges/queues across separate brokers/clusters without merging them into one cluster. Good for:
- Multi-datacenter event distribution
- Aggregating events from edge clusters to a central cluster
rabbitmqctl set_parameter federation-upstream my-upstream \
'{"uri":"amqp://user:pass@remote-broker","expires":3600000}'
rabbitmqctl set_policy federate-orders "^orders\." '{"federation-upstream-set":"all"}'
12.2 Shovel
A simpler, more explicit “pump” that moves messages from a source queue/exchange to a destination — runs as a plugin, good for one-off migrations or simple bridging between brokers, including different RabbitMQ versions.
rabbitmqctl set_parameter shovel my-shovel \
'{"src-uri":"amqp://source","src-queue":"orders.q",
"dest-uri":"amqp://destination","dest-queue":"orders.q"}'
Federation vs Shovel: Federation is topology-aware (mirrors exchange bindings dynamically); Shovel is a fixed point-to-point pipe. Use Shovel for simple, well-defined bridges; Federation for more dynamic multi-cluster topologies.
13. Security
13.1 Users, Vhosts & Permissions
Always scope access with the principle of least privilege:
rabbitmqctl add_vhost orders-service
rabbitmqctl add_user orders_app StrongPassword!
rabbitmqctl set_permissions -p orders-service orders_app "^orders\." "^orders\." "^orders\."
# permissions: configure / write / read (regex patterns)
Never use the default guest user in production — it’s restricted to localhost connections by default for exactly this reason; don’t work around that restriction.
13.2 TLS
Enable TLS for all non-loopback traffic:
listeners.ssl.default = 5671
ssl_options.cacertfile = /path/to/ca.pem
ssl_options.certfile = /path/to/cert.pem
ssl_options.keyfile = /path/to/key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
13.3 Authentication Backends
Beyond internal auth: LDAP, OAuth 2.0 (via rabbitmq_auth_backend_oauth2), x.509 client certs. Use OAuth2/LDAP for centralized identity management in larger orgs.
13.4 Runtime Hardening
- Disable unused plugins and the management UI on production-facing nodes if not needed, or restrict access via firewall/VPN.
- Rotate credentials; avoid embedding them in code — use secret managers.
- Enable
rabbitmq_shovel/federation credentials with scoped, dedicated users, not admin accounts.
14. Management, Monitoring & Observability
14.1 Management Plugin
rabbitmq-plugins enable rabbitmq_management
Provides an HTTP API and UI (default port 15672) for queues, connections, channels, exchanges, and policies.
14.2 Key Metrics to Alert On
- Queue depth / message backlog growth rate — a growing queue means consumers can’t keep up.
- Consumer utilisation (
consumer_utilisationmetric) — low value means consumers are often idle waiting, could indicate downstream bottlenecks or too-low prefetch. - Unacked message count — persistently high values suggest slow/stuck consumers or crashed processes not acking.
- File descriptor / socket usage on the broker.
- Memory & disk alarms (
memory_high_watermark,disk_free_limit) — RabbitMQ blocks all publishers when these are triggered, so alert well before they fire. - Connection/channel churn — frequent reconnects indicate a client bug (e.g., opening a channel per message).
14.3 Prometheus Integration
rabbitmq-plugins enable rabbitmq_prometheus
Exposes metrics on port 15692 for Prometheus scraping — pair with Grafana dashboards (official RabbitMQ Grafana dashboards are published by the team).
14.4 Tracing
rabbitmq_tracing plugin logs message flow for debugging — only enable temporarily in dev/staging, it has real performance overhead.
15. Messaging Patterns
15.1 Work Queue (Task Distribution / Competing Consumers)
Multiple consumers bound to one queue; RabbitMQ round-robins deliveries (subject to prefetch). Use for parallelizable, independent jobs (image resizing, email sending).
15.2 Publish/Subscribe
Fanout exchange → multiple queues, one per subscriber service. Each service gets its own copy of every event.
15.3 Routing (Direct Exchange)
Selective delivery based on exact routing key match — e.g., severity-based log routing (error, warning, info to different queues).
15.4 Topics
Multi-criteria routing using wildcard patterns — e.g., <region>.<service>.<event_type>.
15.5 RPC (Request/Reply)
# Requester
result = channel.queue_declare(queue='', exclusive=True) # anonymous callback queue
callback_queue = result.method.queue
channel.basic_publish(
exchange='', routing_key='rpc_queue',
properties=pika.BasicProperties(reply_to=callback_queue, correlation_id=corr_id),
body=request
)
# consume from callback_queue, match correlation_id, then get response
Caution: RPC over AMQP couples services synchronously and adds latency/complexity. Prefer async event-driven flows where possible; reserve RPC for genuinely synchronous needs.
15.6 Saga Pattern (Distributed Transactions)
Break a distributed transaction into a sequence of local transactions, each publishing an event that triggers the next step. On failure, publish compensating events to undo prior steps.
OrderCreated → PaymentReserved → InventoryReserved → OrderConfirmed
| |
(failure) (failure)
v v
PaymentReleased ←──── InventoryReleaseFailed
Use a choreography (event-driven, no central coordinator) approach for simple flows, or orchestration (a dedicated saga coordinator service) for complex multi-step flows needing visibility/control.
15.7 Transactional Outbox
To avoid the “dual write” problem (DB write + message publish not being atomic), write the event to an outbox table in the same DB transaction as the business change, then use a separate relay process (polling or CDC via Debezium) to publish to RabbitMQ and mark the outbox row as sent. Guarantees at-least-once delivery consistent with the DB state.
15.8 Scatter-Gather
Publish a request to multiple services (fanout) each replying to a shared correlation-tracked reply queue; aggregate responses with a timeout — useful for parallel enrichment/lookup fan-out.
15.9 Circuit Breaker on Consumers
Wrap downstream calls (DB/HTTP) inside consumers with a circuit breaker; on repeated failure, stop consuming (cancel/pause) rather than nacking messages into an infinite requeue loop that hammers a struggling dependency.
16. Connection & Channel Management Best Practices
- One long-lived connection per process/service, not per message. Connections are expensive (TCP + AMQP handshake + heartbeats).
- One channel per thread/coroutine — channels are not thread-safe; never share a channel across concurrent threads without external locking.
- Never open a channel per message — this is the #1 cause of RabbitMQ performance complaints. Reuse channels.
- Use a connection pool in high-concurrency environments, sized to your concurrency level, not per-request.
- Enable heartbeats (default 60s) so dead TCP connections are detected promptly rather than hanging silently.
- Implement automatic reconnect with exponential backoff + jitter — most client libraries (Spring AMQP, amqplib with reconnect wrappers, aio-pika) support this natively or via small wrapper code.
- Handle
channel.closeevents (e.g., due to a protocol error like publishing to a non-existent exchange) by recreating the channel — a closed channel is permanently unusable.
17. Idempotency & Error Handling
RabbitMQ guarantees at-least-once delivery under normal ack-based operation — duplicates will happen (e.g., ack lost after processing but before broker receives it, consumer crash after processing but before ack, network retries). Design for this:
- Use a unique message ID (
message_idor a business key) and track processed IDs in your database/cache (INSERT ... ON CONFLICT DO NOTHING, RedisSETNX, etc.) before applying side effects. - Make business logic operations idempotent where feasible (e.g., “set status to X” rather than “increment counter”).
- On processing failure: distinguish transient errors (network blip, DB timeout — safe to
nack(requeue=True)or retry via DLX/backoff) from permanent errors (malformed payload, business rule violation — route straight to a poison-message queue, don’t infinite-loop retry). - Always cap retries (see §7’s
x-deathcount pattern) to avoid infinite requeue loops burning CPU/network. - Log correlation IDs / message IDs at every hop for traceability across services.
18. Performance Tuning
- Batch acknowledgments where safe (multiple=True) to reduce protocol chatter — but understand the “ack everything up to X” semantics before doing this concurrently.
- Increase prefetch for lightweight/fast tasks; keep it low for heavy/slow tasks (see §6.2).
- Use persistent messages only when necessary — durability costs disk I/O; transient messages in non-durable queues are much faster for use cases where loss is acceptable (metrics, ephemeral events).
- Avoid huge messages (>128KB is a common soft guideline) — for large payloads, publish a reference (e.g., S3 URL) instead of the blob itself.
- Lazy queues / paging: quorum queues and modern classic queues automatically page messages to disk when memory pressure is high — but if you see high queue depth with heavy consumer contention, check disk I/O since paging kicks in.
- Right-size your cluster: quorum queues shine with 3–5 nodes but too many replicas per queue increases Raft overhead — 3 replicas is often the sweet spot even for large clusters (place queue leaders across nodes evenly).
- Avoid too many queues/exchanges on one node — thousands of queues each with their own Erlang process add scheduling overhead; consider queue sharding or consolidating with routing keys instead of one queue per entity.
- Monitor
consumer_utilisationto right-size consumer concurrency instead of guessing. - Use multiple cores: run enough consumer processes/threads to saturate available cores — a single-threaded consumer with high prefetch won’t use concurrency it doesn’t have.
19. Common Pitfalls
| Pitfall | Why it hurts | Fix |
|---|---|---|
| Opening a channel per message | Massive overhead, connection churn | Reuse long-lived channels |
auto_ack=True everywhere | Silent message loss on crash | Manual ack with proper error handling |
| No DLX configured | Rejected/expired messages vanish silently | Always attach a DLX to production queues |
Unbounded prefetch (0/very high) | One consumer hoards messages, unfair distribution, huge redelivery storms on crash | Tune prefetch deliberately |
| Publishing without confirms on critical data | Silent data loss on broker/network failure | Enable publisher confirms |
| Treating RabbitMQ as exactly-once | It’s at-least-once by design | Build idempotent consumers |
| Classic mirrored queues for new HA needs | Deprecated, split-brain prone | Use quorum queues |
| Clustering across WAN | High latency breaks Erlang distribution, partitions | Use Federation/Shovel across WAN instead |
| No queue length/TTL limits | Unbounded memory/disk growth from a stuck consumer | Set x-max-length, TTL, x-overflow |
Using default guest user in prod | Security risk | Dedicated least-privilege users + TLS |
Ignoring x-death retry count | Infinite retry loops on poison messages | Cap retries, route to poison queue |
20. Quick Reference Cheat Sheet
# Durable, quorum queue with DLX and TTL-based retry
channel.queue_declare(
queue='orders.q',
durable=True,
arguments={
'x-queue-type': 'quorum',
'x-dead-letter-exchange': 'orders.dlx',
'x-dead-letter-routing-key': 'orders.retry'
}
)
# Publisher: confirms + persistence
channel.confirm_select()
channel.basic_publish(
exchange='orders.direct',
routing_key='order.created',
body=payload,
properties=pika.BasicProperties(
delivery_mode=2,
content_type='application/json',
message_id=str(uuid4()),
correlation_id=corr_id
),
mandatory=True
)
# Consumer: manual ack + sane prefetch
channel.basic_qos(prefetch_count=50)
channel.basic_consume(queue='orders.q', on_message_callback=handle, auto_ack=False)
Essential rabbitmqctl commands:
rabbitmqctl list_queues name messages consumers memory
rabbitmqctl list_connections
rabbitmqctl list_channels
rabbitmqctl cluster_status
rabbitmqctl set_policy <name> <pattern> '<definition>' --apply-to queues
rabbitmqctl node_health_check
Further Reading
- Official docs: https://www.rabbitmq.com/docs
- Quorum queues: https://www.rabbitmq.com/docs/quorum-queues
- Streams: https://www.rabbitmq.com/docs/streams
- Reliability guide: https://www.rabbitmq.com/docs/reliability