NOVA源码分析——NOVA中的RabbitMQ解析

 本篇文章是由本人阅读NOVA源码过程当中的心得、RabbitMQ的官方文档以及网上的一些资料整理总结而成的,也为了方便之后对这部份内容的复习。正则表达式

    NOVA是OpenStack系统的核心模块,主要负责虚拟机实例的生命周期管理、网络管理(前几个版本)、存储卷管理(前几个版本)、用户管理以及其余相关云平台管理功能,在能力上相似于Amazon EC2和Rackspace Cloud Servers。算法

    消息队列(Queue)与数据库(Database)做为Nova整体架构中的两个重要组成部分,两者经过系统内消息传递和信息共享的方式实现任务之间、模块之间、接口之间的异步部署,在系统层面大大简化了复杂任务的调度流程与模式,是整个OpenStack Nova系统的核心功能模块。终端用户(DevOps、Developers和其余OpenStack组件)主要经过Nova API实现与OpenStack系统的互动,同时Nova守护进程之间经过消息队列和数据库来交换信息以执行API请求,完成终端用户的云服务请求。数据库

    Nova采用无共享、基于消息的灵活架构,意味着Nova的组件有多种安装方式,能够将每一个Nova-Service模块单独安装在一台服务器上,同时也能够根据业务需求将多个模块组合安装在多台服务器上。安全

1.RabbitMQ服务器

    OpenStack Nova系统目前主要采用RabbitMQ做为信息交换中枢。网络

    RabbitMQ是一种处理消息验证、消息转换和消息路由的架构模式,它协调应用程序之间的信息通讯,并使得应用程序或者软件模块之间的相互意识最小化,有效实现解耦。架构

    RabbitMQ适合部署在一个拓扑灵活易扩展的规模化系统环境中,有效保证不一样模块、不一样节点、不一样进程之间消息通讯的时效性;并且,RabbitMQ特有的集群HA安全保障能力能够实现信息枢纽中心的系统级备份,同时单节点具有消息恢复能力,当系统进程崩溃或者节点宕机时,RabbitMQ正在处理的消息队列不会丢失,待节点重启以后可根据消息队列的状态数据以及信息数据及时恢复通讯。负载均衡

    RabbitMQ在功能性、时效性、安全可靠性以及SLA方面的出色能力可有效支持OpenStack云平台系统的规模化部署、弹性扩展、灵活架构以及信息安全的需求。异步

2.AMQP性能

    AMQP是应用层协议的一个开放标准,为面向消息的中间件而设计,其中RabbitMQ是AMQP协议的一个开源实现,OpenStack Nova各软件模块经过AMQP协议实现信息通讯。AMQP协议的设计理念与数据通讯网络中的路由协议很是相似,可概括为基于状态的面向无链接通讯系统模式。不一样的是,数据通讯网络是基于通讯链路的状态决定客户端与服务端之间的连接,而AMQP是基于消息队列的状态决定消息生产者与消息消费者之间的连接。对于AMQP来说,消息队列的状态信息决定通讯系统的转发路径,连接两端之间的链路并非专用且永久的,而是根据消息队列的状态与属性实现信息在RabbitMQ服务器上的存储与转发,正如数据通讯网络的IP数据包转发机制,全部的路由器是基于通讯链路的状态而造成路由表,IP数据包根据路由表实现报文的本地存储与逐级转发,两者在实现机制上具备殊途同归之妙。

    AMQP的目标是实现端到端的信息通讯,那么必然涉及两个基本的概念:AMQP实现通讯的因素是什么以及AMQP实现通讯的实体以及机制是什么。

    AMQP是面向消息的一种应用程序之间的通讯方法,也就是说,“消息”是AMQP实现通讯的基本因素。AMQP有两个核心要素——交换器(Exchange)与队列(Queue)经过消息的绑定与转发机制实现信息通讯。其中,交换器是由消费者应用程序建立,而且可与其余应用程序实现共享服务,其功能与数据通讯网络中的路由器很是类似,即接收消息以后经过路由表将消息准确且安全的转发至相应的消息队列。一台RabbitMQ服务器或者由多台RabbitMQ服务器组成的集群能够存在多个交换器,每一个交换器经过惟一的Exchange ID进行识别。

    交换器根据不一样的应用程序的需求,在生命周期方面也是灵活可变的,主要分为三种:持久交换器、临时交换器与自动删除交换器。持久交换器是在RabbitMQ服务器中长久存在的,并不会由于系统重启或者应用程序终止而消除,其相关数据长期驻留在硬盘之上;临时交换器驻留在内存中,随着系统的关闭而消失;自动删除交换器随着宿主应用程序的停止而自动消亡,可有效释放服务器资源。

    队列也是由消费者应用程序建立,主要用于实现存储与转发交换器发送来的消息,队列同时也具有灵活的生命周期属性配置,可实现队列的持久保存、临时驻留与自动删除。

    由以上能够看出,消息、队列和交换器是构成AMQP的三个关键组件,任何一个组件的实效都会致使信息通讯的中断,所以鉴于三个关键组件的重要性,系统在建立三个组件的同时会打上“Durable”标签,代表在系统重启以后当即恢复业务功能。

    以上主要介绍构成AMQP的三个关键要素,那么它们之间是如何工做的呢?

    由图中能够看出,交换器接收发送端应用程序的消息,经过设定的路由转发表与绑定规则将消息转发至相匹配的消息队列,消息队列继而将接收到的消息转发至对应的接收端应用程序。数据通讯网络经过IP地址造成的路由表实现IP报文的转发,在AMQP环境中的通讯机制也很是相似,交换器经过AMQP消息头(Header)中的路由选择关键字(Routing Key)而造成的绑定规则(Binding)来实现消息的转发,也就是说,“绑定”即链接交换机与消息队列的路由表。消息生产者发送的消息中所带有的Routing Key是交换器转发的判断因素,也就是AMQP中的“IP地址”,交换器获取消息以后提取Routing Key触发路由,经过绑定规则将消息转发至相应队列,消息消费者最后从队列中获取消息。AMQP定义三种不一样类型的交换器:广播式交换器(Fanout Exchange)、直接式交换器(Direct Exchange)和主题式交换器(Topic Exchange),三种交换器实现的绑定规则也有所不一样。

3.Nova中的RabbitMQ应用

3.1RabbitMQ在Nova中的实现

    RabbitMQ是OpenStackNova系统的信息中枢,目前Nova中的各个模块经过RabbitMQ服务器以RPC(远程过程调用)的方式实现通讯,并且各模块之间造成松耦合关联关系,在扩展性、安全性以及性能方面均体现优点。由前文可知,AMQP的交换器有三种类型:Direct、Fanout和Topic,并且消息队列是由消息消费者根据自身的功能与业务需求而生成。

    首先说说三个比较重要的概念:

    交换器:

    接受消息而且将消息转发给队列。在每一个寻你主机的内部,交换器有惟一对应的名字。应用程序在他的权限范围以内能够建立、删除、使用 和共享交换器实例。交换器能够是持久的,临时的或者自动删除的。持久的交换器会一直存在于Server端直到他被显示的删除;临时交换器在服务器关闭时停 止工做;自动删除的交换器在没有应用程序使用它的时候被服务器删除。

    队列:

    “消息队列”,它是一个具名缓冲区,它表明一组消费者应用程序保存消息。这些应用程序在它们的权限范围内能够建立、使用、共享消息队列。相似于交换器,消息队列也能够是持久的,临时的或者自动删除的。临时消息队列在服务器被关闭时中止工做;自动删除队列在没有应用程序使用它的时候被服务器自动删 除。消息队列将消息保存在内存、硬盘或二者的组合之中。消息队列保存消息,并将消息发给一个或多个客户端,特别的消息队列会跟踪消息的获取状况,消息要出对就必须被获取,这样能够阻止多个客户端同时消费同一条消息的状况发生,同时也能够被用来作单个队列多个消费者之间的负载均衡。

    绑定:

    能够理解为交换器和消息队列之间的一种关系,绑定以后交换器会知道应该把消息发给那个队列,绑定的关键字称为binding_key。在程序中咱们这样使用:

    channel.queue_bind(exchange='direct_logs',queue=queue_name,routing_key=binding_key)

    Exchange和Queue的绑定能够是多对多的关系,每一个发送给Exchange的消息都会有一个叫作routing_key的关键字,交换器要想把消息发送给某个特定的队列,那么该队列与交换器的binding_key必须和消息的routing_key相匹配才OK。

 

    介绍一下RabbitMQ的三种类型的交换器:

    广播式交换器类型(fanout

    该类交换器不分析所接收到消息中的Routing Key,默认将消息转发到全部与该交换器绑定的队列中去。广播式交换器转发效率最高,可是安全性较低,消费者应用程序可获取本不属于本身的消息。

    广播交换器是最简单的一种类型,就像咱们从字面上理解到的同样,它把全部接受到的消息广播到全部它所知道的队列中去,不论消息的关键字是什么,消息都会被路由到和该交换器绑定的队列中去。

    它的工做方式以下图所示:

    在程序中申明一个广播式交换器的代码以下:

    channel.exchange_declare(exchange='fanout',type='fanout')

 

    直接式交换器类型(direct

    该类交换器须要精确匹配Routing Key与BindingKey,如消息的Routing Key = Cloud,那么该条消息只能被转发至Binding Key = Cloud的消息队列中去。直接式交换器的转发效率较高,安全性较好,可是缺少灵活性,系统配置量较大。

    相对广播交换器来讲,直接交换器能够给咱们带来更多的灵活性。直接交换器的路由算法很简单——一个消息的routing_key彻底匹配一个队列的 binding_key,就将这个消息路由到该队列。绑定的关键字将队列和交换器绑定到一块儿。当消息的routing_key和多个绑定关键字匹配时消息 可能会被发送到多个队列中。

    咱们经过下图来讲明直接交换器的工做方式:


    如图:Q1,Q2两个队列绑定到了直接交换器X上,Q1的binding_key是“orange”,Q2有两个绑定,一个binding_key是black,另外一个binding_key是green。在这样的关系下,一个带有 “orange” routing_key的消息发送到X交换器以后将会被X路由到队列Q1,一个带有 “black” 或者 “green”  routing_key的消息发送到X交换器以后将会被路由到Q2。而全部其余消息将会被丢失掉。

 

    主题式交换器(Topic Exchange

    该类交换器经过消息的Routing Key与Binding Key的模式匹配,将消息转发至全部符合绑定规则的队列中。Binding Key支持通配符,其中“*”匹配一个词组,“#”匹配多个词组(包括零个)。例如,Binding Key=“*.Cloud.#”可转发Routing Key=“OpenStack.Cloud.GD.GZ”、“OpenStack.Cloud.Beijing”以及“OpenStack.Cloud”的消息,可是对于Routing Key=“Cloud.GZ”的消息是没法匹配的。

    这里的routing_key可使用一种相似正则表达式的形式,可是特殊字符只能是“*”和“#”,“*”表明一个单词,“#”表明0个或是多个单词。这样发送过来的消息若是符合某个queue的routing_key定义的规则,那么就会转发给这个queue。

 

    在Nova中主要实现Direct和Topic两种交换器的应用在系统初始化的过程当中,各个模块基于Direct交换器针对每一条系统消息自动生成多个队列注入RabbitMQ服务器中,依据Direct交换器的特性要求,Binding Key=“MSG-ID”的消息队列只会存储与转发Routing Key=“MSG-ID”的消息。同时,各个模块做为消息消费者基于Topic交换器自动生成两个队列注入RabbitMQ服务器中。

    Nova各个模块之间基于AMQP消息实现通讯,可是真正实现消息调用的应用流程主要是RPC机制。Nova基于RabbitMQ实现两种RPC调用:RPC.CALL和RPC.CAST,其中RPC.CALL基于请求与响应方式,RPC.CAST只是提供单向请求,两种RPC调用方式在Nova中均有不一样的应用场景。

    Nova的各个模块在逻辑功能是能够划分为两种:Invoker和Worker,其中Invoker模块主要功能是向消息队列中发送系统请求消息,如Nova-API和Nova-Scheduler;Worker模块则从消息队列中获取Invoker模块发送的系统请求消息以及向Invoker模块回复系统响应消息,如Nova-Compute、Nova-Volume和Nova-Network。Invoker经过RPC.CALL和RPC.CAST两个进程发送系统请求消息;Worker从消息队列中接收消息,并对RPC.CALL作出响应。Invoker、Worker与RabbitMQ中不一样类型的交换器和队列之间的通讯关系如图所示。

    Nova根据Invoker和Worker之间的通讯关系可逻辑划分为两个交换域:Topic交换域与Direct交换域,两个交换域之间并非严格割裂,在信息通讯的流程上是深度嵌入的关系。Topic交换域中的Topic消息生产者(Nova-API或者Nova-Scheduler)与Topic交换器生成逻辑链接,经过PRC.CALL或者RPC.CAST进程将系统请求消息发往Topic交换器。Topic交换器根据系统请求消息的Routing Key分别送入不一样的消息队列进行转发,若是消息的Routing Key=“NODE-TYPE.NODE-ID”,则将被转发至点对点消息队列;若是消息的Routing Key=“NODE-TYPE”,则将被转发至共享消息队列。Topic消息消费者探测到新消息已进入响应队列,当即从队列中接收消息并调用执行系统消息所请求的应用程序。每个Worker都具备两个Topic消息消费者程序,对应点对点消息队列和共享消息队列,连接点对点消息队列的Topic消息消费者应用程序接收RPC.CALL的远程调用请求,并在执行相关计算任务以后将结果以系统响应消息的方式经过Direct交换器反馈给Direct消息消费者;同时连接共享消息队列的Topic消息消费者应用程序只是接收RPC.CAST的远程调用请求来执行相关的计算任务,并无响应消息反馈。所以,Direct交换域并非独立运做,而是受限于Topic交换域中RPC.CALL的远程调用流程与结果,每个RPC.CALL激活一次Direct消息交换的运做,针对每一条系统响应消息会生成一组相应的消息队列与交换器组合。所以,对于规模化的OpenStack云平台系统来说,Direct交换域会因大量的消息处理而造成整个系统的性能瓶颈点。

3.2Nova系统RPC.CALL以及RPC.CAST调用流程

    由前文能够看出,RPC.CALL是一种双向通讯流程,即Worker程序接收消息生产者生成的系统请求消息,消息消费者通过处理以后将系统相应结果反馈给Invoker程序。

    例如,一个用户经过外部系统将“启动虚拟机”的需求发送给NOVA-API,此时NOVA-API做为消息生产者,将该消息包装为AMQP信息以RPC.CALL方式经过Topic交换器转发至点对点消息队列,此时,Nova-Compute做为消息消费者,接收该信息并经过底层虚拟化软件执行相应虚拟机的启动进程;待用户虚拟机成功启动以后,Nova-Compute做为消息生产者经过Direct交换器和响应的消息队列将“虚拟机启动成功”响应消息反馈给Nova-API,此时Nova-API做为消息消费者接收该消息并通知用户虚拟机启动成功,一次完整的虚拟机启动的RPC.CALL调用流程结束。其具体流程如图所示:


    
    (1)Invoker端生成一个Topic消息生产者和一个Direct消息消费者。其中,Topic消息生产者发送系统请求消息到Topic交换器;Direct消息消费者等待响应消息。

    (2)Topic交换器根据消息的Routing Key转发消息,Topic消费者从相应的消息队列中接收消息,并传递给负责执行相关任务的Worker。

    (3)Worker根据请求消息执行完任务以后,分配一个Direct消息生产者,Direct消息生产者将响应消息发送到Direct交换器。

    (4)Direct交换器根据响应消息的Routing Key转发至相应的消息队列,Direct消费者接收并把它传递给Invoker。

    RPC.CAST的远程调用流程与RPC.CALL相似,只是缺乏了系统消息响应流程。一个Topic消息生产者发送系统请求消息到Topic交换器,Topic交换器根据消息的Routing Key将消息转发至共享消息队列,与共享消息队列相连的全部Topic消费者接收该系统请求消息,并把它传递给响应的Worker进行处理,其调用流程如图所示:

相关文章
相关标签/搜索