What is an exchange, a binding and a routing key? How are exchanges and queues associated with each other? When should I use them and how? This article explains the different types of exchanges in RabbitMQ and scenarios for how you can use them.
Messages are not published directly to a queue, instead, the producer sends messages to an exchange. Exchanges are message routing agents, defined per virtual host within RabbitMQ. An exchange is responsible for the routing of the messages to the different queues. An exchange accepts messages from the producer application and routes them to message queues with help of header attributes, bindings, and routing keys.
A binding is a "link" that you set up to bind a queue to an exchange.
The routing key is a message attribute. The exchange might look at this key when deciding how to route the message to queues (depending on exchange type).
Exchanges, connections, and queues can be configured with parameters such as durable, temporary, and auto delete upon creation. Durable exchanges will survive server restarts and will last until they are explicitly deleted. Temporary exchanges exist until RabbitMQ is shut down. Auto-deleted exchanges are removed once the last bound object unbound from the exchange.
In RabbitMQ, there are four different types of exchange that route the message differently using different parameters and bindings setups. Clients can create their own exchanges or use the predefined default exchanges, the exchanges created when the server starts for the first time.
Standard RabbitMQ message flow
- The producer publishes a message to the exchange.
- The exchange receives the message and is now responsible for the routing of the message.
- A binding has to be set up between the queue and the exchange. In this case, we have bindings to two different queues from the exchange. The exchange routes the message in to the queues.
- The messages stay in the queue until they are handled by a consumer
- The consumer handles the message.
If you are not familiar with RabbitMQ and message queueing, I would recommend you to read RabbitMQ for beginners - what is RabbitMQ? before you start to read about exchanges, routing keys, headers, and bindings.
A direct exchange delivers messages to queues based on a message routing key. The routing key is a message attribute added into the message header by the producer. The routing key can be seen as an "address" that the exchange is using to decide how to route the message. A message goes to the queue(s) whose binding key exactly matches the routing key of the message.
The direct exchange type is useful when you would like to distinguish messages published to the same exchange using a simple string identifier.
The default exchange AMQP brokers must provide for the direct exchange is "amq.direct".
Imagine that queue A (create_pdf_queue) in the image below (Direct Exchange Figure) is bound to a direct exchange (pdf_events) with the binding key pdf_create. When a new message with routing key pdf_create arrives at the direct exchange, the exchange routes it to the queue where the binding_key = routing_key, in the case to queue A (create_pdf_queue).
- Exchange: pdf_events
- Queue A: create_pdf_queue
- Binding key between exchange (pdf_events) and Queue A (create_pdf_queue): pdf_create
- Exchange: pdf_events
- Queue B: pdf_log_queue
- Binding key between exchange (pdf_events) and Queue B (pdf_log_queue): pdf_log
Example: A message with routing key pdf_log is sent to the exchange pdf_events. The messages is routed to pdf_log_queue because the routing key (pdf_log) match the binding key (pdf_log).
If the message routing key does not match any binding key, the message will be discarded.
The default exchange is a pre-declared direct exchange with no name, usually referred by the empty string "". When you use the default exchange, your message will be delivered to the queue with a name equal to the routing key of the message. Every queue is automatically bound to the default exchange with a routing key which is the same as the queue name.
Topic exchanges route messages to queues based on wildcard matches between the routing key and something called the routing pattern specified by the queue binding. Messages are routed to one or many queues based on a matching between a message routing key and this pattern.
The routing key must be a list of words, delimited by a period (.), examples are agreements.us and agreements.eu.stockholm which in this case identifies agreements that are set up for a company with offices in lots of different locations. The routing patterns may contain an asterisk (“*”) to match a word in a specific position of the routing key (e.g. a routing pattern of "agreements.*.*.b.*" will only match routing keys where the first word is "agreements" and the fourth word is "b"). A pound symbol (“#”) indicates match on zero or more words (e.g. a routing pattern of "agreements.eu.berlin.#" matches any routing keys beginning with "agreements.eu.berlin").
The consumers indicate which topics they are interested in (like subscribing to a feed for an individual tag). The consumer creates a queue and sets up a binding with a given routing pattern to the exchange. All messages with a routing key that match the routing pattern will be routed to the queue and stay there until the consumer consumes the message.
The default exchange AMQP brokers must provide for the topic exchange is "amq.topic".
The image to the right show an example where consumer A is interested in all the agreements in Berlin.
- Exchange: agreements
- Queue A: berlin_agreements
- Routing pattern between exchange (agreements) and Queue A (berlin_agreements): agreements.eu.berlin.#
- Example of message routing key that will match: agreements.eu.berlin and agreements.eu.berlin.headstore
Consumer B is interested in all the agreements.
- Exchange: agreements
- Queue B: all_agreements
- Routing pattern between exchange (agreements) and Queue B (all_agreements): agreements.#
- Example of message routing key that will match: agreements.eu.berlin and agreements.us
Consumer C is interested in all agreements for European head stores.
- Exchange: agreements
- Queue C: headstore_agreements
- Routing pattern between exchange (agreements) and Queue C (headstore_agreements): agreements.eu.*.headstore
- Example of message routing keys that will match: agreements.eu.berlin.headstore and agreements.eu.stockholm.headstore
A message with routing key agreements.eu.berlin is sent to the exchange agreements. The messages are routed to the queue berlin_agreements because the routing pattern of "agreements.eu.berlin.#" matches any routing keys beginning with "agreements.eu.berlin". The message is also routed to the queue all_agreements because the routing key (agreements.eu.berlin) match the routing pattern (agreements.#).
The fanout copies and routes a received message to all queues that are bound to it regardless of routing keys or pattern matching as with direct and topic exchanges. Keys provided will simply be ignored.
Fanout exchanges can be useful when the same message needs to be sent to one or more queues with consumers who may process the same message in different ways.
The image to the right (Fanout Exchange Figure) shows an example where a message received by the exchange is copied and routed to all three queues that are bound to the exchange. It could be sport or weather news updates that should be sent out to each connected mobile device when something happens.
The default exchange AMQP brokers must provide for the topic exchange is "amq.fanout".
- Exchange: sport_news
- Queue A: Mobile client queue A
- Binding: Binging between the exchange (sport_news) and Queue A (Mobile client queue A)
A message is sent to the exchange sport_news. The message is routed to all queues (Queue A, Queue B, Queue C) because all queues are bound to the exchange. Provided routing keys are ignored.
Headers exchanges route based on arguments containing headers and optional values. Headers exchanges are very similar to topic exchanges, but it routes based on header values instead of routing keys. A message is considered matching if the value of the header equals the value specified upon binding.
A special argument named "x-match" tells if all headers must match or just one. The "x-match" property can the have two different values: "any" or "all", where "all" is the default value. A value of "all" means all header pairs (key, value) must match and a value of "any" means at least one of the header pairs must match. Headers can be constructed using a wider range of data types - integer or hash for example instead of a string. The headers exchange type (used with "any") is useful for directing messages which may contain a subset of known (unordered) criteria.
The default exchange AMQP brokers must provide for the topic exchange is "amq.headers".
- Exchange: Binding to Queue A with arguments (key = value): format = pdf, type = report
- Exchange: Binding to Queue B with arguments (key = value): format = pdf, type = log
- Exchange: Binding to Queue C with arguments (key = value): format = zip, type = report
Message 1 is published to the exchange with header arguments (key = value): "format = pdf", "type = report" and "x-match = all"
Message 1 is delivered to Queue A - all key/value pair match
Message 2 is published to the exchange with header arguments of (key = value): "format = pdf" and "x-match = any"
Message 2 is delivered to Queue A and Queue B - the queue is configured to match any of the headers (format or log).
Message 3 is published to the exchange with header arguments of (key = value): "format = zip", "type = log" and "x-match = all"
Message 3 is not delivered to any queue - the queue is configured to match all of the headers (format or log).
Dead Letter Exchange
If no matching queue can be found for the message, the message will be silently dropped. RabbitMQ provides an AMQP extension known as the "Dead Letter Exchange" - the dead letter exchange provides functionality to capture messages that are not deliverable.
Please email us at email@example.com if you have any suggestions about missing content or other feedback.