Skip to content

Core Concepts

A client talks to RabbitMQ over a single long-lived TCP connection. But opening a TCP connection is expensive, and a busy app needs to do many things at once — publish from one place, consume in another, several threads working in parallel.

The answer is channels. A channel is a lightweight virtual connection multiplexed over one TCP connection. You open one connection per process and many channels over it — typically one channel per thread or per concurrent task.

flowchart LR
  app["Application"] --> conn["1 TCP Connection"]
  conn --> ch1["Channel 1
(publisher)"]
  conn --> ch2["Channel 2
(consumer)"]
  conn --> ch3["Channel 3
(consumer)"]
  ch1 --> broker["RabbitMQ"]
  ch2 --> broker
  ch3 --> broker
One connection, many channels

The rule of thumb: connections are long-lived and few; channels are cheap and many. A common mistake is opening a connection per operation — that exhausts the broker. Another is sharing one channel across threads — channels are not thread-safe. One connection per app, one channel per worker.

You met these last lesson; here is the precise version:

  • An exchange receives every published message and, using its type and its bindings, decides which queues get it. It stores nothing.
  • A queue is an ordered buffer that holds messages until a consumer takes them. This is the only thing that stores messages.
  • A binding connects an exchange to a queue. It may carry a binding key that, combined with the message’s routing key, determines whether a message matches.

The routing key is just a string label the producer puts on each message (like order.created or payment.failed). Whether it matters depends on the exchange type — a fanout exchange ignores it entirely, a topic exchange pattern-matches on it. That’s the whole Exchanges & Routing module.

Put it together and trace a single message end to end:

sequenceDiagram
  participant P as Producer
  participant X as Exchange
  participant Q as Queue
  participant C as Consumer
  P->>X: publish(routing key, body)
  X->>Q: route by binding
  Note over Q: message waits here
  Q->>C: deliver
  C->>C: process
  C->>Q: ack (I'm done)
  Note over Q: message removed
A message from publish to acknowledgement
  1. The producer publishes a message with a routing key to an exchange.
  2. The exchange routes it to matching queues (zero, one, or many).
  3. The message waits in the queue until a consumer is ready.
  4. The queue delivers it to a consumer.
  5. The consumer processes it and sends an acknowledgement — only then does the queue delete it.

That last step is the safety net: until the consumer acks, RabbitMQ assumes the work isn’t done. If the consumer crashes mid-task, the un-acked message is redelivered. (An entire later lesson is devoted to acknowledgements — they are how “at-least-once” delivery works.)

What is a channel in RabbitMQ?
What is the recommended connection/channel usage?
Which component actually stores messages?
When is a message finally removed from a queue?