Nova如何应用AMQP(RPC)详解--以RabbitMQ为例

xiaoxiao2022-06-11  29

我的博客:http://www.sskywatcher.com/blog/

翻译自openstack官网文章,英文原文:https://docs.openstack.org/nova/latest/reference/rpc.html

目录

简述

Nova RPC 导览

Topic Publisher

Direct Consumer

Topic Consumer

Direct Publisher

Topic Exchange

Direct Exchange

Queue Element

RPC Call调用流程详解

RPC Cast调用流程详解

AMQP Broker 的负载

API 调用的吞吐量

Workers的总个数

使用RabbitMQ 的一些参数解释



简述


AMQP 是OpenStack云管平台选用的消息队列系统。(默认是 RabbitMQ),位于任意两个Nova组件之间的AMQP broker允许他们以一个松耦合的方式进行通信。更严谨地说,Nova组件之间使用远程过程调用(Remote Procedure Calls ,下文中直接以RPC 简称)互相通信;这是基于publish/subscribe范例构建的,使用该模式能获得以下有利之处:

客户端与服务端解耦;异步调用(一个客户端发起的远程调用并不要求服务端同时工作).对远程调用的的随机负载均衡(架设有多个服务端处于线上且正常工作, 单向的调用可以被透明的传递至第一个可用的服务端。

Nova使用了direct、fanout以及topic-based三种类型的exchanges. 下图展示了其基本结构:

图1  NOVA中使用RPC的结构

Nova 通过提供一个适配器的类来实现了基于AMQP的RPC (包括请求-回复型调用和单向调用,代表性的别名是rpc.call和 rpc.cast) ,这个类包括marshaling and unmarshaling of messages 到一个函数调用中。每一个Nova服务 (比如Compute, Scheduler等等) 都会在初始化的时候创建2个queues,一个用来接受routing keys 是NODE-TYPE.NODE-ID (比如 compute.hostname)的消息,另一个 用来接收routing keys为通用的 NODE-TYPE (例如compute)的消息。前者是当Nova-API需要将命令重定向到指定的节点时专用的,比如如下命令: openstack server delete $instance。该情况下,只有实际运行该虚拟机的计算节点才能杀掉该instance。  当RPC调用模式为请求-恢复模式时,API作为一个consumer,其余情况则仅仅扮演publisher。


Nova RPC 导览


下图展示了一个单独的部署在OpenStack云中的实例内部的broker节点 (图中被称为一个RabbitMQ节点) 。 每个 Nova 组件都连接到该消息broker,根据其私有属性 (比如一个计算节点或者一个网络节点)的不同可能作为不同的角色使用不同的队列其可以作为一个Invoker (比如 API 或者Scheduler) 或者一个 Worker (比如 Compute 或者Network)。Invokers和Workers并不会真是存在于Nova对象模型中,但是为了便于表达的更加清晰,我们将使用这种抽象。一个 Invoker 可以使用不同方法来通过队列系统发送消息: 1) rpc.call 以及 ii) rpc.cast; 而一个 Worker 则可以使用 rpc.call 才做来通过队列系统接收并回复一个消息。

图2展示了下列内部元素:

Topic Publisher

当执行rpc.call 或者 rpc.cast 操作时会激活一个Topic Publisher; 该对象被实例化并且用来向队列系统push一条消息。 每个publisher 总是连接到同一个基于topic的exchange;该对象的生命周期仅限于消息传递期间。

Direct Consumer

只有在执行rpc.call操作时才会产生一个Direct Consumer;该对象被实例化用来向队列系统接收一条消息。每一个consumer 通过专用且唯一的队列的连接到一个唯一的direct-based exchange,该对象的生命周期同样仅限于消息传递期间。exchange和 queue 的标识符由一个UUID生成器决定,该标识符将被封装在消息中然后被Topic Publisher发送出去 (仅 rpc.call操作)。

Topic Consumer

当一个Worker被初始化之后,即会产生一个Topic Consumer,在worker生存期间该Consumer会一直存在;这个对象备用接收消息后执行Worker定义的适当的动作。 一个Topic Consumer可以通过共享队列或者唯一专用的队列连接到topic-based exchange。Nova中每个Worker拥有2个Topic Consumers, 其中一个仅可以在 rpc.cast 操作期间被寻址 (该consumer 连接到一个使用topic最为exchange key 的共享队列) ,另一个则仅可以在 rpc.call 操作期间被寻址 (该consumer 连接到一个使用 topic.host 作为exchange key的唯一专用专用队列)。

Direct Publisher

当执行rpc.call操作时可以产生一个Direct Publisher ,该对象被实例化用来返回请求-回复类型操作的需要的消息。该对象连接到一个根据当前消息指定的direct-based 的exchange。

Topic Exchange

一个Exchange 是指存在于虚拟主机上下文中的路由表 (比如RabbitMQ中提供的多租户机制);它的类型(比如:topic类型、. direct类型)决定了路由策略 。一个broker 节点对Nova中的每个topic只有一个基于topic的exchange。

Direct Exchange

执行 rpc.call 操作时生成的路由表;在一个message broker node的生命周期中,将会有非常多该类型的exchange被实例化, 每引用一次rpc.call 时就会产生一个。

Queue Element

一个Queue 是一个存放消息的桶。消息被存放在桶中,知道有一个consumer(无论是Topic 或者 Direct 类型的Consumer)连接到这个队列并且取走它。Queues 可以使共享的也可以是专用的。 以topic为路由键的Queues 十倍多个同属性的Workers 所共享的队列。

图2  RabbitMQ的架构

RPC Call调用流程详解


下图完成描述了执行rpc.call操作的消息流:

初始化一个Topic Publisher来发送消息请求到队列系统;在真正执行消息发布的动作前,立即创建一个Direct Consumer来等待回复消息;一旦消息被exchange解包, 它就会被routing key (例如 ‘topic.host’)中指定的Topic Consumer获取,并且被传递到负责该任务的Worker;一旦该任务完成,就会生成一个Direct Publisher来发送回复消息到队列系统;一旦消息被exchange解包,它就会被routing key (例如 ‘msg_id’)中指定的 Direct Consumer 获取,并且被传递到Invoker。 图3  rpc.call操作的消息流

RPC Cast调用流程详解


下图完成描述了执行rpc.cast操作的消息流:

初始化一个Topic Publisher来发送消息请求到队列系统;一旦消息被exchange解包,它就会被routing key (例如 ‘topic’)中指定的Topic Consumer 获取,并且被传递到负责该任务的Worker;


AMQP Broker 的负载


在任意给定时间对于一个 broker node(比如运行RabbitMQ),它的负载可以用以下参数来有效衡量:

API 调用的吞吐量

OpenStack云提供的API调用的数量(更准确的说是rpc.call ops) 取决于direct-based exchanges, 相关的队列以及连接到这些exchanges的direct consumers。

Workers的总个数

相同属性的一组workers会共享一个队列;同样还存在非常多取决于workers数的专用队列;workers的数量同样决定了他们所共享的 topic-based 的exchange内部routing key的数量。

下图展示了一个测试环境中启动了Nov组件后RabbitMQ 节点的状态。Nova创建的Exchanges和queues包括:

Exchanges nova (topic exchange)Queues compute.phantom (phantom 是主机名)computenetwork.phantom (phantom 是主机名)networkscheduler.phantom (phantom 是主机名)scheduler


使用RabbitMQ 的一些参数解释


Nova 使用 Kombu 来连接到 RabbitMQ 环境。 Kombu是一个基于AMQPLib的Python库,这个库实现AMQP 0.8 标准。使用Kombu时,Invokers 和 Workers 需要指定以下的参数用来初始化一个连接到RabbitMQ的连接对象 (以下大多数素材可以在Kombu documentation中找到):

hostname

一个AMQP服务器的主机名。

userid

用于向该服务器认证的用户名

password

用于向该服务器认证的密码

virtual_host

一起工作的虚拟主机的名字,该虚拟主机必须在该服务器上存在,并且用户具有接入能力。默认是 “/”.

port

AMQP服务器的端口号,默认是5672 (amqp).

以下列出的参数都是默认的

insist

保持会话,在一个配置了多实例的服务器中,该选项高质服务器保证客户端连接段特定的服务器上, 默认值是False。

connect_timeout

客户端房企连接服务器的超时时间,默认不超时。

ssl

是否使用SSl,默认值是False。


以下参数用来更精确的将消息传递到消费者端:

connection

上文提到的连接对象

queue

队列的名称

exchange

队列即被绑定到exchange名称

routing_key

对于routing key的准确意义需要基于exchange_type 属性

Direct exchange

如果消息的routing key属性和队列的routing_key属性值相同,那么将消息发送到该队列。

Fanout exchange

消息被发送到绑定到该exchange的所有队列,即使该绑定关系没有制定任何key。

Topic exchange

如果一条消息的routing key属性匹配routing key of the key according to a primitive pattern matching scheme, 那么该消息将被传递至该队列。消息的 routing key 有一系列被点号 (.)分割的单词组成,还可以使用个特殊字符: *、#。* 匹配一个至多个的任意单词, #匹配零个至多个任意单词。 例如: *.stock.# 可以匹配 usd.stock 和 eur.stock.db,但是不能匹配stock.nasdaq.

durable

该选项决定了exchange和queue是否持久化。 持久化的 exchange 和 quene 在 RabbitMQ 重启后会保持在 active 状态。对应的,非持久化的 exchange 和 quene 在 RabbitMQ 重启后会被清除,单独指定队列(quene)为持久化,而 exchange 为非持久化是无意义无价值的,因为持久化的队列不能被绑定到非持久化的 exchange 上,默认值是 True .

auto_delete

如果设置为 True, 所有队列的工作完成后删除该 exchange,默认值是 False.

exclusive

专用队列只能被当前连接消息, 该开关开启时,相当于同时开启了auto_delete。默认值是 False。

exchange_type

AMQP 定义了多个不同的exchange类型 (应用不同的路由算法),基本上覆盖了绝大多数应用场景。

auto_ack

消息被接收后自动完成确认机制。默认值是 False,接收者需要处理确认。

no_ack

禁用服务器侧的确认机制。 该选项不同于 auto_ack i选项同时关闭了两侧的确认机制。该功能以牺牲可靠性的方式提升了性能。 如果在消息还没有送达应用之前客户端挂了,那么消息有可能会丢失。

auto_declare

如果该值被设为True并且设置了exchange名字,在服务器初始化的时候会自动声明该exchange,该选项默认开启。

delivery_mode

消息的默认传递模式。值为一个int,RabbitMQ支持一下模式 :

1 (临时)

消息是非持久化的,消息的数据只会被保存在内存中,如果服务器重启或者挂掉会导致消息丢失 。

2 (永久)

消息是持久化的。消息同时存放在内存和硬盘中,在服务器挂掉或者重启依然能够保护数据不被丢失。

默认配置的模式时 2 (persistent)在消息发送操作中,消息的发布者可以指定模式来覆盖默认配置,例如可以在一个持久化的队列中发送非持久化的消息。

转载请注明原文地址: https://www.6miu.com/read-4931484.html

最新回复(0)