AMQP Exchanges

Theoretically, the exchange is the first entry point for a message entering the message broker. Messages are not published directly to a queue. Instead, the producer sends messages to an exchange, which you can think of as a message routing agent. An exchange does not operate alone. Bindings and Routing Keys are needed for the exchange to correctly address messages to queues.

What are Exchanges, Routing Keys, and Bindings?

Exchanges are message routing agents responsible for routing messages to the addressed queue with the help of header attributes, bindings, and routing keys. A binding is a link between the queue and the exchange, and the routing key is the message attribute validated by the exchange when deciding how to route the message to queues.

What benefits do Exchanges have?

There are four types of exchanges (Direct, Fanout, Topic and Header) and these can all be configured with different properties. These types and properties decide on how messages behave and make the application extremely tweakable.

How should I use Exchanges?

Depending on the use case, exchanges should be configured carefully to meet your demand. The different exchange types and the properties given to them decide what specific queue a message should be delivered.

Exchange Life-Cycle

LavinMQ includes four types of exchanges, each routing the message differently depending on the setup of the bindings and parameters. Predefined default exchanges are created on the first server startup, however, clients can also create their own exchanges.

  1. Declare is when the client tells the server to make sure the exchange exists. The exchange is created if it does not already exist.
  2. A binding between the exchange and the queue is created.
  3. The client publishes messages to the exchange.
  4. The exchange may be deleted by the client.

Clients can create their own exchanges or use the predefined default exchanges which are created when the server starts for the first time.

Binding between queues and exchanges are created using:

channel.queue_bind(exchange=exchange_name,queue=queue_name)

The four exchange types

This video explains the use of exchanges and exchange types in a relatable example using a taxi service company:

Video exchanges to come

Direct Exchange

The Direct Exchange directs the message to a specific queue by looking at the routing key. The routing key in the message is compared for equality with routing keys on bindings. The direct exchange type is useful to distinguish messages published to the same exchange using a simple string identifier.

The direct exchange and AMQP broker must provide “amq.direct”.

channel.exchange_declare(exchange='direct_test', exchange_type='direct')

Default exchange (Nameless Direct Exchange)

A pre-declared Direct Exchange that has no name, the default exchange is typically referred to by an empty string. When you use default exchange, your message is delivered to the queue with a name equal to the routing key of the message. Queues are automatically bound to the default exchange using a routing key that is the same as the name of the queue.

Topic Exchange

Topic Exchange routes messages to one or many queues by looking at the routing key. The routing key in the message is compared for matches with routing key patterns on the bindings. All messages with a routing key that match the routing pattern are routed to the queue and stay there until the consumer consumes the message.

The topic exchange supports strict routing key matching, like a direct exchange, but will also perform wild-card matching using * and # as placeholders. Routing keys must delimit the list of words by a period.

The Topic Exchange and AMQP broker must provide amq.topic.

Fanout Exchange

Disregarding the routing keys, a fanout exchanges copies and routes messages received to all queues bound to it. A provided routing key is simply 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 Fanout Exchange and AMQP broker must provide “amq.fanout”.

channel.exchange_declare(exchange='exchange_logs', exchange_type='fanout')

Headers Exchange

Messages are routed based on arguments with optional values and headers in a headers exchange. Headers exchanges are very similar to topic exchanges, but route messages based on header values instead of routing keys. A message matches if the value of the header equals the value specified upon binding.

The x-match argument is added in the binding between queue and exchange, which specifies if one header or all headers must match. Either any common header between the message and the binding count as a match, or all the headers referenced in the binding need to be present in the message for it to match. The “x-match” property can 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, while a value of “any” means at least one of the header pairs must match. Data types including integer or hash are used to construct headers instead of a string. Using a binding argument of ‘any’, the headers exchange type is versatile for directing messages containing unordered known criteria.

The headers Exchange an AMQP broker must provide “amq.headers”.

channel.exchange_declare(exchange='exchange_test', type='headers')

Exchange Properties and Arguments

Exchanges can be configured with properties and arguments which are used to define their behaviors. Properties are defined by the AMQP protocol and included in LavinMQ. Arguments are optional and can be any key-value pair and are used for feature extensions.

Durable Exchange

An exchange can be marked as durable or transient. Durable exchanges survive server restarts and exist until they are explicitly deleted.

Code example of how to declare a durable queue:

channel.queue_declare(queue='hello', durable=True)

A temporary exchange exists until LavinMQ is shut down.

Auto-deleted exchange

Once the last bound object has been unbound from the exchange, auto-deleted exchanges are removed.

The server ignore the auto-delete field if the exchange already exists.

Internal exchange

The internal exchange defines if the exchange only should be used in exchange to exchange bindings only.

Alternate exchange

An alternate_exchange can be created to handle unroutable messages. The exchange decides to what exchange messages should be sent if the message is unroutable from the current one.

Exchange Arguments

Arguments can be any key-value pair and are used for feature extensions. All arguments are optional and will be described under the section Other Exchanges Types. Popular Exchange Arguments are:

  • x-dead-letter-exchange
  • x-dead-letter-routing-key
  • delayed exchange
  • etc.

Exchange to Exchange Bindings

It is possible to add bindings between exchanges in LavinMQ. This operation could be needed when you need to save messages for automatically deleted queues, load balance topics within a single broker, or send the same messages to different exchange types.

LavinMQ exchange to exchange binding allows you to create complex topologies without federating brokers. The binding works well when using topics, automatically deleted exchanges, or serving messages to queues with different needs.

Code example in Python:

channel.exchange_bind(destination='destination_exchange',source='source_exchange')

Other exchange types

Following exchanges are additional presets to the AMQ 0.9.1 protocol:

Delay messages with Delayed Message Exchange

It is possible to delay the delivery of messages for a certain time so that subscribers don’t see them immediately.

Read about Delayed Message Exchange.

Load-balance messages with the Consistent Hash Exchange

The consistent hash exchange allows you to use an exchange to load-balance messages between queues and can be used if you need to get the maximum use of many cores. Based on the hash of the routing key, the exchange routes messages to all queues that are bound to it.

Read about Consistent Hash Exchange.

Redirect a message after expiration or rejection with the Dead lettering Exchange

A queue that is declared with the x-dead-letter-exchange property will send messages which are either rejected, nacked, or expired (with TTL) to the specified dead-letter-exchange.

Read about Dead lettering Exchange.

Rewind messages with the Persistent Exchange

The persistent exchange keeps the message in the exchange even after the message has been routed to all queue bindings.

A persistent exchange stores all messages coming into the exchange even though there are no queue bindings on that exchange. This differs from other exchanges where messages will be dropped if the exchange doesn’t have any bindings.

Read about Dead lettering Exchange.