Cloud Native programming with Golang
上QQ阅读APP看书,第一时间看更新

The Advanced Message Queueing Protocol

On a protocol level, RabbitMQ implements the AMQP. Before getting started with RabbitMQ, let's get started by taking a look at the basic protocol semantics of AMQP.

An AMQP message broker manages two basic kinds of resources—Exchanges and Queues. Each publisher publishes its messages into an exchange. Each subscriber consumes a queue. The AMQP broker is responsible for putting the messages that are published in an exchange into the respective queue. Where messages go after they have been published to an exchange depends on the exchange type and the routing rules called bindings. AMQP knows three different types of exchanges:

  •  Direct exchanges: Messages are published with a given topic (called routing key in AMQP) that is a simple string value. Bindings between a direct exchange and queue can be defined to match exactly that topic.
  • Fanout exchanges: Messages are routed to all queues that are connected to a fanout exchange via a binding. Messages can have a routing key, but it will be ignored. Every bound queue will receive all messages that are published in the fanout exchange.
  • Topic exchanges: This works similar to direct exchanges. However, queues are now bound to the exchange using patterns that the message's routing key must match. Topic exchanges usually assume routing keys to be segmented with the dot character '.'. As an example, your routing keys could follow the "<entityname>.<state-change>.<location>" pattern (for example, "event.created.europe"). You can now create queue bindings that may contain wildcards using the '*' or '#' characters. * will match any single routing key segment, whereas # will match any number of segments. So, for the preceding example, valid bindings might be as follows:
    •  event.created.europe (obviously)
    • event.created.* (listen to whenever an event is created anywhere in the world)
    •  event.# (listen to whenever any change is made to an event anywhere in the world)
    •  event.*.europe (listen to whenever any change is made to an event in Europe)

One possible example exchange and queue topology are shown in the next diagram. In this case, we have one service that publishes messages, the EventService. We have two queues in which messages will be routed. The first queue, evts_booking, will receive any and all messages that are related to any kind of change made to an event. The second queue, evts_search, will receive messages only regarding the creation of new events. Note that the evts_booking queue has two subscribers. When two or more subscribers subscribe to the same queue, the message broker will dispatch messages to one of the subscribers on a rotating basis.

Message broker displaying messages to one of the subscribers on a rotating basis

It is important to note that the entire AMQP topology (meaning all the exchanges and queues and how they are bound to one another) is not defined by the broker, but by the publishers and consumers themselves. AMQP specifies several methods that clients can use to declare the exchanges and queues they need. For example, a publisher would typically use the exchange.declare method to assert that the exchange it wants to publish actually exists (the broker will then create it if it did not exist before). On the other hand, a subscriber might use the queue.declare and queue.bind methods to declare a queue that it wants to subscribe and bind it to an exchange.

There are multiple open source message brokers that implement AMQP. One of the most popular ones (and also the one that we will be working within this chapter) is the RabbitMQ broker, an open source AMQP broker developed by Pivotal and made available under the Mozilla Public License. Other message brokers that implement AMQP are Apache QPID (https://qpid.apache.org) and Apache ActiveMQ (http://activemq.apache.org).

Although we will use RabbitMQ in this example, the code written in this chapter should work will all kinds of AMQP implementations.