AMQP is the messaging technology chosen by the OpenStack cloud. The AMQP broker, default to Rabbitmq, sits between any two Nova components and allows them to communicate in a loosely coupled fashion. More precisely, Nova components (the compute fabric of OpenStack) use Remote Procedure Calls (RPC hereinafter) to communicate to one another; however such a paradigm is built atop the publish/subscribe paradigm so that the following benefits can be achieved:
Nova uses direct, fanout, and topic-based exchanges. The architecture looks like the one depicted in the figure below:
Nova implements RPC (both request+response, and one-way, respectively nicknamed
rpc.call
and rpc.cast
) over AMQP by providing an adapter class which
take cares of marshaling and unmarshaling of messages into function calls. Each
Nova service (for example Compute, Scheduler, etc.) create two queues at the
initialization time, one which accepts messages with routing keys
NODE-TYPE.NODE-ID
(for example compute.hostname
) and another, which
accepts messages with routing keys as generic NODE-TYPE
(for example
compute
). The former is used specifically when Nova-API needs to redirect
commands to a specific node like openstack server delete $instance
. In this
case, only the compute node whose host's hypervisor is running the virtual
machine can kill the instance. The API acts as a consumer when RPC calls are
request/response, otherwise it acts as a publisher only.
The figure below shows the internals of a message broker node (referred to as a
RabbitMQ node in the diagrams) when a single instance is deployed and shared in
an OpenStack cloud. Every Nova component connects to the message broker and,
depending on its personality (for example a compute node or a network node),
may use the queue either as an Invoker (such as API or Scheduler) or a Worker
(such as Compute or Network). Invokers and Workers do not actually exist in the
Nova object model, but we are going to use them as an abstraction for sake of
clarity. An Invoker is a component that sends messages in the queuing system
via two operations: 1) rpc.call
and ii) rpc.cast
; a Worker is a
component that receives messages from the queuing system and reply accordingly
to rpc.call
operations.
Figure 2 shows the following internal elements:
rpc.call
or an rpc.cast
operation is executed; this object is instantiated and used to push a message
to the queuing system. Every publisher connects always to the same
topic-based exchange; its life-cycle is limited to the message delivery.rpc.call
operation is
executed; this object is instantiated and used to receive a response message
from the queuing system. Every consumer connects to a unique direct-based
exchange via a unique exclusive queue; its life-cycle is limited to the
message delivery; the exchange and queue identifiers are determined by a UUID
generator, and are marshaled in the message sent by the Topic Publisher (only
rpc.call
operations).rpc.cast
operations (and it connects to
a shared queue whose exchange key is topic
) and the other that is
addressed only during rpc.call
operations (and it connects to a unique
queue whose exchange key is topic.host
).rpc.call
operations and it
is instantiated to return the message required by the request/response
operation. The object connects to a direct-based exchange whose identity is
dictated by the incoming message.rpc.call
operations; there
are many instances of this kind of exchange throughout the life-cycle of a
message broker node, one for each rpc.call
invoked.topic
are
shared amongst Workers of the same personality.The diagram below shows the message flow during an rpc.call
operation:
msg_id
) and passed to the
Invoker.The diagram below shows the message flow during an rpc.cast
operation:
At any given time the load of a message broker node running RabbitMQ etc is function of the following parameters:
rpc.call
ops) being served by the
OpenStack cloud dictates the number of direct-based exchanges, related queues
and direct consumers connected to them.The figure below shows the status of a RabbitMQ node after Nova components' bootstrap in a test environment. Exchanges and queues being created by Nova components are:
compute.phantom
(phantom
is hostname)compute
network.phantom
(phantom
is hostname)network
scheduler.phantom
(phantom
is hostname)scheduler
Nova uses Kombu to connect to the RabbitMQ environment. Kombu is a Python library that in turn uses AMQPLib, a library that implements the standard AMQP 0.8 at the time of writing. When using Kombu, Invokers and Workers need the following parameters in order to instantiate a Connection object that connects to the RabbitMQ server (please note that most of the following material can be also found in the Kombu documentation; it has been summarized and revised here for sake of clarity):
hostname
userid
password
virtual_host
port
5672
(amqp).The following parameters are default:
insist
connect_timeout
ssl
More precisely Consumers need the following parameters:
connection
queue
exchange
routing_key
The interpretation of the routing key depends on the value of the
exchange_type
attribute.
routing_key
attribute of
the queue are identical, then the message is forwarded to the queue..
, like domain names), and two special characters
are available; star (*
) and hash (#
). The star matches any word,
and the hash matches zero or more words. For example .stock.#
matches
the routing keys usd.stock
and eur.stock.db
but not
stock.nasdaq
.durable
auto_delete
exclusive
auto_delete
.
Default is False.exchange_type
auto_ack
auto_ack
is set to False, and the receiver is required to manually
handle acknowledgment.no_ack
auto_ack
in that acknowledgment is turned off altogether. This
functionality increases performance but at the cost of reliability. Messages
can get lost if a client dies before it can deliver them to the application.auto_declare
Publishers specify most the parameters of Consumers (such as they do not specify a queue name), but they can also specify the following:
delivery_mode
The default delivery mode used for messages. The value is an integer. The following delivery modes are supported by RabbitMQ:
1
(transient)2
(persistent)The default value is 2
(persistent). During a send operation, Publishers
can override the delivery mode of messages so that, for example, transient
messages can be sent over a durable queue.
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.