OpenStack之AMQP原理介绍(Rabbit MQ)

本人虾悠悠  QQ:617600535 邮箱:leezhoucloud@gmail.com,欢迎交流。 

      关于这部分内容,相当的重要,弄清楚这部分的通信机制,有利于我们学到OpenStack的核心内容,比如创建一个实例的时候,请求是如何一层层发送到compute-api-->scheduler-->compute-run_instance的,所以,务必请大家耐心把这篇文章看完。

       最近为了组件之间到底是如何通信,如何收发消息,我看了N多关于RPC的学习资料,但是回过头来看看OpenStack官网的关于AMQP的描述,其实这才是学习RPC机制最好的资料,特地把这篇文章分享给大家。

官方网址:http://docs.openstack.org/developer/nova/devref/rpc.html


开始之前,先介绍一下关于OpenStack的重要概念(转):

Nova中的每个组件都会连接消息服务器,一个组件可能是一个消息发送者(API、Scheduler),也可能是一个消息接收者(Compute、Volume、Network)。发送消息有两种方式:同步调用(rpc.call)和异步调用(rpc.cast)。OpenStack中默认使用kombu(实现AMQP协议的Python函数库)连接RabbitMQ服务器。消息的收/发者都需要一个Connetion对象连接RabbitMQ服务器。

异步调用解释:你女友喊你回家吃饭,你说知道了,等下忙完找你一起吃饭,然后你女友继续忙其它事情了。

同步调用解释:你女友喊你回家吃饭,你说知道了,然后你女友就在那里等啊等,等你忙完了,一起吃饭。(感动啊~~)

请求与响应:rpc.call   只有请求:rpc.cast


以下是对AMQP机制的翻译:

        OpenStack中的Nova各个服务之间以松耦合的方式使用AMQP进行通信(RPC)。使用AMQP的发布/订阅模式来进行RPC有如下优势:

         1) 客户端及服务端之间解耦:客户端不需要知道有哪些服务端以及服务端的地址;

         2) 客户端与服务端之间完全的同步性:客户端的RPC不需要服务端正好在运行;

         3) 远程调用的随机均衡:如果有多个服务端在运行,单向RPC会透明的分发到最一个可用的服务端。

 Nova用到了directfanouttopic三种类型的交换器(Exchange),整个架构图如下:

 

增加几个概念,看下面rabbitMQ框架图来看下面几点。

(1) routing-key: 可以理解为与exchange交换器相关
     binding-key: 可以理解为与queue队列相关

(2) fanout,说明只要与交换器相连的所有queue都可以接收消息

(3) direct,routing-key =  binding-key,意为只有当交换器和队列匹配上了,交换器的消息才能发送到队列中

(4) topic,binding-key = "*.cloud.#",binding-key有点类似一个正则表达式,routing-key只要和binding-key匹配上,交换器的数据就可以传到对应binding-key的队列上。


Nova基于AMQP实现了两种类型的RPC:

rpc.call:请求响应类型,一个请求发送出去以后,需要等待响应;调用需要指定目标服务结点;

rpc.cast:单向RPC,只将请求发送出去,不需要等待结果;不关心请求由哪个服务结点完成。

 

Nova RPC结构

       下图说明了Nova RPC机制的主要结成部分(以使用RabbitMQ为例)。从调用关系上来说,Nova服务要么以调用方(Invoker:API,Scheduler)角色来使用消息队列,要么以服务方(Worker:Compute,Volume,Network)角色来使用消息队列。调用方通过rpc.call和rpc.cast发送消息;服务方从消息队列接收消息,并对rpc.call请求做出响应。

         从生命周期上来说,TopicPublisher,DirectConsumer,DirectPublisher三部分是在rpc.call发起的时候创建的;而TopicConsumer是Nova的各个服务在启动时创建,并在服务结束时销毁。Nova中每一个服务在启动时会创建两个TopicConsumer:一个以NODE-TYPE为交换器(Exchange Key),一个以NODE-TYPE.HOST为交换器。

#事实上,我们在前面一篇《nova service 启动代码分析》中看到,同时有3个consumer被创建了起来,

#但是第3个,self.conn.create_consumer(self.topic, rpc_dispatcher, fanout=True) ,fanout=true

#的这个只用在Scheduler更新,所以基本上不用考虑。

 

1、rpc.call调用流程(比较少用到)

         1). 初始化一个TopicPublisher,发送消息到消息队列;在发送消息之前,初始化一个DirectConsumer(以消息ID为交换器的名称),用于等待响应消息;

         2). 消息被交换器分发到NODE-TYPE.HOST消息队列(下图中的topic.host),并被相应服务结点(根据host确定)的TopicConsumer获取到;

         3). 服务结点根据消息内容(调用函数及参数)调用相应服务;调用完成后,初始化一个DirectPublisher,并根据消息ID将响应消息发送到相应的消息队列;

         4). 响应消息被调用方的DirectConsumer获取到,调用完成。

                                                 注意:图中红色箭头的数据流走向


大笑rpc.cast调用流程(经常会被用到)

        1). 初始化一个TopicPublisher,并将消息发送到消息队列;

       2). 消息被交换器分发到NODE-TYPE消息队列(下图中的topic),并被相应服务的结点TopicConsumer获取到,然后根据消息内容调用相应服务完成调用。

                                                             注意:图中红色箭头的数据流走向


至此,官方的AMQP的基本原理介绍完了,在下一篇博文中,我们将会继续介绍,如何实现两个组件之间的通信(消息传递)。

阅读更多

没有更多推荐了,返回首页