, have labored with machine studying or large-scale information pipelines, likelihood is you’ve used some form of queueing system.
Queues let providers speak 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 penning this? I just lately 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 believed it’d be value sharing what I discovered.
Hope you will discover this handy!
A fast primer: queues vs request-response mannequin
Microservices usually talk in two types — the basic 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 the hours of darkness 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 completed, whereas queues affirm instantly 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 customers (receivers). First launched in 2007 and written in Erlang, it implements AMQP (Superior Message Queuing Protocol), an open commonplace for structuring, routing, and acknowledging messages.
Consider it like a submit workplace for distributed methods: functions drop off messages, RabbitMQ kinds them into queues, and customers decide them up when prepared.
A typical pairing within the Python world is Celery + RabbitMQ: RabbitMQ brokers the duties, whereas Celery staff execute them within the background.
In containerised setups, RabbitMQ usually runs in its personal container, whereas Celery staff run in separate containers you could scale independently.
The way it works at a excessive degree
Your app desires to run some work asynchronously. Since this activity would possibly 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.
- Trade: 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 patron — 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 shopper (if one is offered) over a TCP connection.
Core elements 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 is connected to a message by the producer.
- A binding secret is 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, that means it might probably hear for a number of totally different routing keys or patterns.
2. Exchanges
An trade in RabbitMQ is sort of a site 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 a precise tackle 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 will probably be copied to all of them (1:many).
2b) Fanout trade
A fanout trade is like shouting via a loudspeaker.
Each message is copied to all queues certain to the trade. The routing keys are ignored, and it’s all the time a 1:many broadcast.
Fanout exchanges will be helpful when the identical message must be despatched to a number of queues with customers 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 corresponding to "order.*”. Which means that 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 would possibly find yourself in only one queue or in a number of on the similar time.
There are two vital particular instances 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.

second) Headers trade
A headers trade is like sorting mail by labels as an alternative of addresses.
As a substitute of trying on the routing key (like "order.accomplished"), the trade inspects the headers of a message: These are key–worth pairs connected as metadata. For example:
x-match: all, precedence: excessive, kind: electronic mail→ the queue will solely get messages which have eachprecedence=excessiveandkind=electronic mail.x-match: any, area: us, area: eu→ the queue will get messages the place no less than one of the circumstances is true (area=usorarea=eu).
The x-match subject is what determines whether or not all guidelines should match or anyone rule is sufficient.
As a result of a number of queues can every declare their very own header guidelines, a single message would possibly 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 apply, however they’re helpful when routing depends upon extra advanced enterprise logic. For instance, you would possibly need to ship a message provided that customer_tier=premium, message_format=json, or area=apac .
2e) Useless letter trade
A useless letter trade is a security web for undeliverable messages.
3. A push supply mannequin
Which means that as quickly as a message enters a queue, the dealer will push it out to a shopper that’s subscribed and prepared. The shopper 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 customers as quickly as doable.
Helpful options in Rabbit MQ
Rabbit MQ’s structure enables you to form message circulate to suit your workload. Listed here are some helpful patterns.
Work queues — competing customers sample
You publish duties into one queue, and many customers (eg. celery staff) all take heed to that queue. The dealer delivers every message to precisely one shopper, so staff “compete” for work. This implicitly interprets to easy load-balancing.
If you happen to’re on celery, you’ll need to maintain worker_prefetch_multiplier=1 . What this implies is {that a} employee will solely fetch one message at a time, avoiding sluggish staff from hoarding duties.
Pub/sub sample
A number of queues certain 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 totally different customers can course of the identical occasion in numerous methods.
Specific acknowledgements
RabbitMQ makes use of express acknowledgements (ACKs) to ensure dependable supply. An ACK is a affirmation despatched from the patron again to the dealer as soon as a message has been efficiently processed.
When a shopper sends an ACK, the dealer removes that message from the queue. If the patron NACKs or dies earlier than ACKing, RabbitMQ can redeliver (requeue) the message or route it to a useless letter queue for inspection or retry.
There may be, nevertheless, an vital 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 truly executes it. This behaviour (acks_late=False, which is the default) implies that if a employee crashes halfway via working the duty, the dealer has already been advised the message was dealt with and gained’t redeliver it.
Precedence queues
RabbitMQ has a out of the field precedence queueing characteristic which lets greater precedence messages soar the road. Underneath the hood, the dealer creates an inside sub-queue for every precedence degree outlined on a queue.
For instance, when you configure 5 precedence ranges, RabbitMQ maintains 5 inside sub-queues. Inside every degree, messages are nonetheless consumed in FIFO order, however when customers are prepared, RabbitMQ will all the time 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) robotically expires stale messages; and delayed supply is offered by way of plugins (e.g., delayed-message trade) while you want scheduled execution.
Methods 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 discovered about how Celery actually makes use of RabbitMQ beneath the hood — and the right way to 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 staff
It additionally handles serialisation (JSON, Pickle, YAML, or customized codecs) so duties will be encoded and decoded throughout the wire.
Celery occasions and the celeryev Trade

Celery consists of an occasion system that tracks employee and activity state. Internally, occasions are printed to a particular subject trade referred to as celeryev.
There are two such occasion sorts:
- Employee occasions eg.
employee.on-line,employee.heartbeat,employee.offlineare all the time on and are light-weight liveliness indicators. - Process occasions, eg.
task-received,task-started,task-succeeded,task-failedthat are disabled by default except the-Eflag is added.
You have got fantastic grain management over each sorts of occasions. You possibly can flip off employee occasions (by turning off gossip, extra on that under) whereas turning on activity occasions.
Gossip
Gossip is Celery’s mechanism for staff to “chat” about cluster state — who’s alive, who simply joined, who dropped out, and sometimes elect a pacesetter 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 certain to the
celeryevsubject trade with the routing key sampleemployee.#.
As a result of each employee subscribes to each employee.* occasion, the site visitors grows shortly because the cluster scales.
With N staff, each 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 staff, that meant a single heartbeat was duplicated 99 occasions. Throughout deployments — when staff 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 out of the blue 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 beneath the brink, RabbitMQ resumes regular operation.
Nonetheless, which means that in the course of the reminiscence spike the dealer turns into unusable — successfully inflicting downtime. You gained’t need that in manufacturing!
The answer is to disable Gossip so staff 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 staff to synchronise state — issues like revoked duties and logical clocks. This occurs solely as soon as, throughout employee boot. If you happen to don’t want this coordination, it’s also possible to 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 might 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 and reconnect and resume consuming duties.
So long as your queues are configured accurately, messages are not misplaced:
sturdy=True(queue survives dealer restart)delivery_mode=2(persistent messages)- Customers ship express ACKs to verify profitable processing
If a connection drops earlier than a activity 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 apply, occasional drops are fantastic, so long as they continue to be rare and queue depth doesn’t construct up.
To finish off
That’s all people, these are a number of the key classes I’ve discovered working RabbitMQ + Celery in manufacturing. I hope this deep dive has helped you higher perceive how issues work beneath the hood. You probably have extra ideas, I’d love to listen to them within the feedback and do attain out!!

