, have labored with machine studying or large-scale knowledge pipelines, chances are high you’ve used some form of queueing system.
Queues let providers discuss to one another asynchronously: you ship off work, don’t wait round, and let one other system decide it up when prepared. That is important when your duties aren’t prompt — suppose long-running mannequin coaching jobs, batch ETL pipelines, and even processing requests for LLMs that take minutes per question.
So why am I scripting this? I not too long ago migrated a manufacturing queueing setup to RabbitMQ, ran right into a bunch of bugs, and located that documentation was skinny on the trickier components. After a good bit of trial and error, I assumed it’d be value sharing what I realized.
Hope you will see that this handy!
A fast primer: queues vs request-response mannequin
Microservices usually talk in two kinds — the traditional request–response mannequin, or the extra versatile queue-based mannequin.
Think about ordering pizza. In a request–response mannequin, you inform the waiter your order after which wait. He disappears, and thirty minutes later your pizza exhibits up — however you’ve been left at nighttime the entire time.
In a queue-based mannequin, the waiter repeats your order, offers you a quantity, and drops it into the kitchen’s queue. Now it’s being dealt with, and also you’re free to do one thing else until the chef will get to it.
That’s the distinction: request–response retains you blocked till the work is finished, whereas queues verify straight away and let the work occur within the background.
What’s Rabbit MQ?
RabbitMQ is a well-liked open-source message dealer that ensures messages are reliably delivered from producers (senders) to shoppers (receivers). First launched in 2007 and written in Erlang, it implements AMQP (Superior Message Queuing Protocol), an open customary for structuring, routing, and acknowledging messages.
Consider it like a put up workplace for distributed methods: functions drop off messages, RabbitMQ kinds them into queues, and shoppers decide them up when prepared.
A standard pairing within the Python world is Celery + RabbitMQ: RabbitMQ brokers the duties, whereas Celery employees execute them within the background.
In containerised setups, RabbitMQ usually runs in its personal container, whereas Celery employees run in separate containers that you would be able to scale independently.
The way it works at a excessive degree
Your app desires to run some work asynchronously. Since this process may take some time, you don’t need the app to take a seat idle ready. As a substitute, it creates a message describing the duty and sends it to RabbitMQ.
- Alternate: This lives inside RabbitMQ. It doesn’t retailer messages however simply decides the place every message ought to go primarily based on guidelines you set (routing keys and bindings).
Producers publish messages to an trade, which acts as a routing middleman. - Queues: They’re like mailboxes. As soon as the trade decides which queue(s) a message ought to go to, it sits there until it’s picked up.
- Shopper: The service that reads and processes messages from a queue. In a Celery setup, the Celery employee is the buyer — it pulls duties off the queue and does the precise work.
As soon as the message is routed right into a queue, the RabbitMQ dealer pushes it out to a client (if one is obtainable) over a TCP connection.
Core parts in Rabbit MQ
1. Routing and binding keys
Routing and binding keys work collectively to resolve the place a message finally ends up.
- A routing secret’s hooked up to a message by the producer.
- A binding secret’s the rule a queue declares when it connects (binds) to an trade.
A binding defines the hyperlink between an trade and a queue.
When a message is distributed, the trade appears to be like on the message’s routing key. If that routing key matches the binding key of a queue, the message is delivered to that queue.
A message can solely have one routing key.
A queue can have one or a number of binding keys, which means it could actually pay attention for a number of completely different routing keys or patterns.
2. Exchanges
An trade in RabbitMQ is sort of a visitors controller. It receives messages, doesn’t retailer messages, and it’s key job is to resolve which queue(s) the message ought to go to, primarily based on guidelines.
If the routing key of a message doesn’t match any the binding keys of any queues, it is not going to get delivered.
There are a number of sorts of exchanges, every with its personal routing fashion.
2a) Direct trade
Consider a direct trade like an actual handle supply. The trade appears to be like for queues with binding keys that precisely match the routing key.
- If just one queue matches, the message will solely be despatched there (1:1).
- If a number of queues have the identical binding key, the message can be copied to all of them (1:many).
2b) Fanout trade
A fanout trade is like shouting by a loudspeaker.
Each message is copied to all queues sure to the trade. The routing keys are ignored, and it’s at all times a 1:many broadcast.
Fanout exchanges might be helpful when the identical message must be despatched to a number of queues with shoppers who could course of the identical message in numerous methods.
2c) Matter trade
A subject trade works like a subscription system with classes.
Each message has a routing key, for instance "order.accomplished”
. Queues can then subscribe to patterns reminiscent of "order.*”
. Which means every time a message is expounded to an order, will probably be delivered to any queues which have subscribed to that class.
Relying on the patterns, a message may find yourself in only one queue or in a number of on the similar time.
There are two essential particular circumstances for binding keys:
*
(star) matches precisely one phrase within the routing key.#
(hash) matches zero or extra phrases.
Let’s illustrate this to make the syntax alot extra intuitive.

2nd) Headers trade
A headers trade is like sorting mail by labels as an alternative of addresses.
As a substitute of wanting on the routing key (like "order.accomplished"
), the trade inspects the headers of a message: These are key–worth pairs hooked up as metadata. As an example:
x-match: all, precedence: excessive, sort: e mail
→ the queue will solely get messages which have eachprecedence=excessive
andsort=e mail
.x-match: any, area: us, area: eu
→ the queue will get messages the place a minimum of one of the circumstances is true (area=us
orarea=eu
).
The x-match
subject is what determines whether or not all guidelines should match or anybody rule is sufficient.
As a result of a number of queues can every declare their very own header guidelines, a single message may find yourself in only one queue (1:1) or in a number of queues without delay (1:many).
Headers exchanges are much less frequent in observe, however they’re helpful when routing will depend on extra complicated enterprise logic. For instance, you may wish to ship a message provided that customer_tier=premium
, message_format=json
, or area=apac
.
2e) Useless letter trade
A lifeless letter trade is a security internet for undeliverable messages.
3. A push supply mannequin
Which means as quickly as a message enters a queue, the dealer will push it out to a client that’s subscribed and prepared. The client doesn’t request messages and as an alternative simply listens on the queue.
This push strategy is nice for low-latency supply — messages get to shoppers as quickly as doable.
Helpful options in Rabbit MQ
Rabbit MQ’s structure allows you to form message movement to suit your workload. Listed here are some helpful patterns.
Work queues — competing shoppers sample
You publish duties into one queue, and many shoppers (eg. celery employees) all take heed to that queue. The dealer delivers every message to precisely one client, so employees “compete” for work. This implicitly interprets to easy load-balancing.
When you’re on celery, you’ll wish to maintain worker_prefetch_multiplier=1
. What this implies is {that a} employee will solely fetch one message at a time, avoiding sluggish employees from hoarding duties.
Pub/sub sample
A number of queues sure to an trade and every queue will get a copy of the message (fanout or subject exchanges). Since every queue will get its personal message copy, so completely different shoppers can course of the identical occasion in numerous methods.
Express acknowledgements
RabbitMQ makes use of specific acknowledgements (ACKs) to ensure dependable supply. An ACK is a affirmation despatched from the buyer again to the dealer as soon as a message has been efficiently processed.
When a client sends an ACK, the dealer removes that message from the queue. If the buyer NACKs or dies earlier than ACKing, RabbitMQ can redeliver (requeue) the message or route it to a lifeless letter queue for inspection or retry.
There may be, nonetheless, an essential nuance when utilizing Celery. Celery does ship acknowledgements by default, but it surely sends them early — proper after a employee receives the duty, earlier than it really executes it. This behaviour (acks_late=False
, which is the default) implies that if a employee crashes halfway by operating the duty, the dealer has already been advised the message was dealt with and received’t redeliver it.
Precedence queues
RabbitMQ has a out of the field precedence queueing function which lets increased precedence messages bounce the road. Beneath the hood, the dealer creates an inner sub-queue for every precedence degree outlined on a queue.
For instance, if you happen to configure 5 precedence ranges, RabbitMQ maintains 5 inner sub-queues. Inside every degree, messages are nonetheless consumed in FIFO order, however when shoppers are prepared, RabbitMQ will at all times attempt to ship messages from higher-priority sub-queues first.
Doing so implicitly would imply an rising quantity of overhead if there have been many precedence ranges. Rabbit MQ’s docs note that though priorities between 1 and 255 are supported, values between 1 and 5 are highly recommended.
Message TTL & scheduled deliveries
Message TTL (per-message or per-queue) mechanically expires stale messages; and delayed supply is obtainable by way of plugins (e.g., delayed-message trade) if you want scheduled execution.
The best way to optimise your Rabbit MQ and Celery setup
If you deploy Celery with RabbitMQ, you’ll discover a couple of “thriller” queues and exchanges showing within the RabbitMQ administration dashboard. These aren’t errors — they’re a part of Celery’s internals.
After a couple of painful rounds of trial and error, right here’s what I realized about how Celery actually makes use of RabbitMQ below the hood — and tune it correctly.
Kombu
Celery depends on Kombu, a Python messaging framework. Kombu abstracts away the low-level AMQP operations, giving Celery a high-level API to:
- Declare queues and exchanges
- Publish messages (duties)
- Devour messages in employees
It additionally handles serialisation (JSON, Pickle, YAML, or customized codecs) so duties might be encoded and decoded throughout the wire.
Celery occasions and the celeryev
Alternate

Celery contains an occasion system that tracks employee and process state. Internally, occasions are revealed to a particular subject trade known as celeryev
.
There are two such occasion varieties:
- Employee occasions eg.
employee.on-line
,employee.heartbeat
,employee.offline
are at all times on and are light-weight liveliness indicators. - Activity occasions, eg.
task-received
,task-started
,task-succeeded
,task-failed
that are disabled by default until the-E
flag is added.
You’ve got nice grain management over each sorts of occasions. You’ll be able to flip off employee occasions (by turning off gossip, extra on that beneath) whereas turning on process occasions.
Gossip
Gossip is Celery’s mechanism for employees to “chat” about cluster state — who’s alive, who simply joined, who dropped out, and sometimes elect a frontrunner for coordination. It’s helpful for debugging or ad-hoc cluster coordination.
By default, Gossip is enabled. When a employee begins:
- It creates an unique, auto-delete queue only for itself.
- That queue is sure to the
celeryev
subject trade with the routing key sampleemployee.#
.
As a result of each employee subscribes to each employee.*
occasion, the visitors grows shortly because the cluster scales.
With N employees, every one publishes its personal heartbeat, and RabbitMQ followers that message out to the opposite N-1 gossip queues. In impact, you get an N × (N-1) fan-out sample.
In my setup with 100 employees, that meant a single heartbeat was duplicated 99 occasions. Throughout deployments — when employees have been spinning up and shutting down, producing a burst of be a part of, depart, and heartbeat occasions — the sample spiraled uncontrolled. The celeryev
trade was all of a sudden dealing with 7–8k messages per second, pushing RabbitMQ previous its reminiscence watermark and leaving the cluster in a degraded state.
When this reminiscence restrict is exceeded, RabbitMQ blocks publishers till utilization drops. As soon as reminiscence falls again below the brink, RabbitMQ resumes regular operation.
Nevertheless, because of this through the reminiscence spike the dealer turns into unusable — successfully inflicting downtime. You received’t need that in manufacturing!
The answer is to disable Gossip so employees don’t bind to employee.#
. You are able to do this within the docker compose the place the employees are spun up.
celery -A myapp employee --without-gossip
Mingle
Mingle is a employee startup step the place the brand new employee contacts different employees to synchronise state — issues like revoked duties and logical clocks. This occurs solely as soon as, throughout employee boot. When you don’t want this coordination, you can even disable it with --without-mingle
Occasional connection drops
In manufacturing, connections between Celery and RabbitMQ can sometimes drop — for instance, as a consequence of a quick community blip. You probably have monitoring in place, you may even see these as transient errors.
The excellent news is that these drops are often recoverable. Celery depends on Kombu, which incorporates computerized connection retry logic. When a connection fails, the employee will try to reconnect and resume consuming duties.
So long as your queues are configured appropriately, messages are not misplaced:
sturdy=True
(queue survives dealer restart)delivery_mode=2
(persistent messages)- Customers ship specific ACKs to verify profitable processing
If a connection drops earlier than a process is acknowledged, RabbitMQ will safely requeue it for supply as soon as the employee reconnects.
As soon as the connection is re-established, the employee continues regular operation. In observe, occasional drops are nice, so long as they continue to be rare and queue depth doesn’t construct up.
To finish off
That’s all of us, these are a number of the key classes I’ve realized operating RabbitMQ + Celery in manufacturing. I hope this deep dive has helped you higher perceive how issues work below the hood. You probably have extra ideas, I’d love to listen to them within the feedback and do attain out!!