Nova中的RabbitMQ
最近需要在项目中使用到消息队列,虽然之前有过使用经验,但是因为当时并无具体要求,所以只是在看完RabbitMQ的tutorial之后就自行实现了,并不知道合理性和效率如何。所以在新的项目设计时,打算借鉴一下Openstack中AMQP的使用经验
Nova消息队列
Openstack云选用AMQP作为其消息技术。AMQP代理,不管是RabbitMQ或者Qpid,位于两个Nova组件之间并允许它们以松耦合的风格通信。更准确地说,Nova组件之间使用RPC通信。但是这个机制是建立在发布/订阅机制之上的,它有以下优势:
解耦client和Server(Client不需要知道Server的具体引用)
Client和Server完全异步(Client不需要Server在收到RPC call时同时执行)
随机平衡远程调用(多个Servant时,优先分配空闲者处理调用)
Nova使用direct, fanout,和topic Exchange。其架构如下图所示
Nova通过一个封装和解封装消息到调用的类来实现RPC功能,包括请求/响应(rpc.call), 单向(rpc.cast)。每个Nova服务(计算,调度等)在初始化时都实现两个AMQP队列,一个接收带有NODE-TYPE.NODE-ID的routingkey(如 compute.hostname)的消息,另一个接受通用的带有NODE-TYPE的routingkey(如compute)的消息。前者将消息传递给特定的物理节点,例如,终止某个虚拟机实例时使用。这个API在调用是“请求/响应”类型时充当consumer,rpc.cast时仅为publisher。
Nova的RPC映射
下图当Openstack云只部署和共享了一个实例时,消息Broker节点(RabbitMQ节点)的内部结构。
每一个Nova组件都连接到消息Broker,根据它的特性(如计算节点还是网络节点),将队列当作Invoker(如调度器,API),或者是Worker(如计算,网络)。Invoker,Worker并不实际存在于Nova对象模型中,我们只是用它便于表述。
Invoker通过两个操作将消息发送到消息队列中:
rpc.call
rpc.cast
Worker组件接收来自队列的消息,并对rpc.call进行回复。
图中组件的说明:
Topic Publisher
每当有任意call或者cast类型的调用时被激活,发送消息到队列中。每个Publisher问题连接到同一个Topic类型的Exchange,它的生命周期限于消息传递。
Direct Consumer
只在rpc.call操作时激活,接收来自队列的消息。每个consumer通过专用Queue连接到唯一的direct Exchange,生命周期限于消息传递。Exchange和Queue的身份标识由UUID生成器决定,封装于Topic Publisher发布的消息中(限rpc.call)。
Topic Consumer
Worker初始化时即激活并在生命周期内存在。它接收消息并调用与Worker角色适应的动作。Topic consumer通过专一或共有的Queue连接唯一的Topic Exchange,每个Worker都有两个topic consumer,一个只接收rpc.cast操作(连接到共享的topic
exchange),另一个接收rpc.call操作(连接到唯一的topic.host
exchange)。
opic Exchange
位于虚拟主机(RabbitMQ提供的多租房机制)间的路由表,其类型(如topic, direct)决定路由策略。一个消息代理只有一个topic Exchange处理所有Nova中的Topic。
Direct Exchange
rpc.call期间创建的路由表,在消息代理节点生命周期内将有许多这种类型的Exchange实例,和每个rpc.call对应。
Queue Element
在消息被处理前缓存消息,可以是专用的也可以是共用的,具有同种属性的Worker共享使用相同routing Key的Topic队列。
RPC Call
下图为rpc.call时消息的流向。
Topic Publisher发送消息到队列系统,在此之前一个Direct Consumer被初始化以等待此次调用的响应。
当消息被传递到Exchange,路由到routingKey(如topic.host)对应的consumer,并由负责这个任务的worker处理。
任务处理完后,分配一个Direct publisher来发送响应到队列系统。
当消息被Exchange分发以后,被具有对应routingKey(如msg_id)的consumer接收并传递给最初的RPC调用者。
RPC Cast
下图为rpc.cast时消息的流向。
使用topic publisher来发送消息到队列系统
消息到达Exchange后,由有对应routingKey的topic consumer获取并传递给负责处理的Worker。
AMQP代理负载
任何时候,消息代理的负载是以下参数的函数:
API调用的吞吐量: Openstack云提供的rpc.call数量影响direct Exchange的数量,以及关联的Queue及consumer数量 。
Worker数量: 相同属性的Wokers共享一个Queue,但是也有和Worker同样数量的专属Queue。Worker数量影响共享的Topic Exchange内的routingKey数量。
下图展示了RabbitMQ节点Nova组件的bootstrap在测试环境中的状态。Exchange和queue包括:
Exchange:
- Nova(topic exchange)
Queue:
compute.phantom
compute
network.phantom
network
scheduler.phantom
scheduler