什么是RPC
RPC即Remote Procedure Call(远程方法调用),是Openstack中一种用来实现跨进程(或者跨机器)的通信机制。Openstack中同项目内(如nova, neutron, cinder...)各服务(service)及通过RPC实现彼此间通信。Openstack中还有另外两种跨进程的通信方式:数据库和Rest API。
Openstack中服务主要以进程的形式实现。也可以以线程的形式实现,但是Python中的线程是协作模型,无法发挥系统中多CPU(或多CPU核心)的能力。
RCP只定义了一个通信接口,其底层的实现可以各不相同。目前Openstack中的主要采用AMQP来实现。AMQP(
Advanced Message Queuing Protocol)是一种基于队列的可靠消息服务协议,具体可参考 http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol。作为一种通信协议,AMQP同样存在多个实现,如Apache Qpid, RabbitMQ等。
AMQP概念
publisher:消息发布者
receiver:消息接收者
queue:
用来保存消息的存储空间,消息没有被receiver前,保存在队列中。
exchange:
exchange是一个很重要的概念。用来接收publisher发出的消息,并决定消息后续处理。后续处理取决于消息的路由算法,而路由算法又是由exchange type决定的。
exchange type:
AMQP中定义的类型包括:direct, topic, headers and fanout。
direct:消息路由到满足此条件的队列中(queue,可以有多个): routing key = binding key
topic:消息路由到满足此条件的队列中(queue,可以有多个):routing key 匹配 binding pattern. binding pattern是类似正则表达式的字符串,可以满足复杂的路由条件。
fanout:消息路由到多有绑定到该exchange的队列中。
Openstack RPC中主要用了这三种exchange type。
binding :
binding是用来描述exchange和queue之间的关系的概念,一个exchang可以绑定多个队列,这些关系由binding建立。前面说的binding key /binding pattern也是在binding中给出。
每个receiver在接收消息前都需要创建binding。
其他:
一个队列可以有多个receiver,队列里的一个消息只能发给一个receiver。至于发送给哪个receiver,由AMQP的负载算法决定,默认为在所有receiver中均匀发送(round robin).
一个消息可以被发送到一个队列中,也可以被发送到多个多列中。多队列情况下,一个消息可以被多个receiver收到并处理。Openstack RPC中这两种情况都会用到。
关于AMQP的概念,可以参考这篇文章:
https://www.rabbitmq.com/tutorials/amqp-concepts.html
Openstack RPC中沿用了AMQP中的一些概念,并做了一些扩展。理解AMQP概念是理解RPC的前提。RPC中定义了如下主要概念:
Server:同AMQP中的receiver
Client:同AMQP中的publisher
Exchange:同AMQP中的exchange
Topic:同AMQP中的topic exchange type。topic类似于面向对象中的class概念,一个topic下可以包括多个方法,client通过topic调用一个方法,server也通过监听topic来提供方法调用。通常每个topic下的方法应该是逻辑上密切相关的,正如class的设计一样。
下面几个概念是RPC扩展的:
Namespace:用来组织server中的方法(method),默认是null。
Method:及被调用的方法,和普通(本地)方法调用中的方法是一个概念。
API version:用来标识server中方法的版本。随着时间的推移,server中的方法可能不断变化,提供版本信息可以保持对之前client的兼容。
Transport:对RPC的底层实现机制的抽象。
Opentack RPC的设计应该借鉴了Sun RPC的设计,Sun RPC的介绍可以参看 http://en.wikipedia.org/wiki/Open_Network_Computing_Remote_Procedure_Call
Sun RPC应该是RPC系统的最早商用实现,理解了这个系统,其他的各种RPC系统也就自然理解了。
这篇文章中详细介绍了Openstack RPC中的基本概念及API设计: https://wiki.openstack.org/wiki/Oslo/Messaging
RPC的使用场景
Openstack中RPC的主要使用场景:
随机调用某server上的一个方法:
Invoke Method on One of Multiple Servers
这个应该是Openstack中最常用的一种RPC调用,每个方法都会有多个server来提供,client调用时由底层机制选择一个server来处理这个调用请求。
像nova-scheduler, nova-conductor都可以以这种多部署方式提供服务。
这种场景通过AMQP的topic exchange实现。
所有server在binding中为binding key指定一个相同的topic, client在调用时使用这个topic既可实现。
调用某特定server上的一个方法:
Invoke Method on a Specific Server
一般Openstack中的各种scheduler会以这种方式调用。通常scheduler都会先选定一个节点,然后调用该节点上的服务。
这种场景通过AMQP的topic exchange实现。
每个server在binding中为其binding key指定一个自己都有的topic, client在调用时使用这个topic既可实现。
调用所有server上的一个方法:
Invoke Method on all of Multiple Servers
这种其实就是一个广播系统。就像开会议,台上的人讲话,台下的人都能听到。
Openstack中有些rpcapi.py的某些方法带有fanout=True参数,这些都是让所有server处理某个请求的情况。
例子: neutron中所有plugin都会有一个AgentNotifierApi,这个rpc是用来调用安装在compute上的L2 agent。因为存在多个L2 agent(每个compute上都会有),所以要用广播模式。
这种场景通过AMQP的fanout exchange实现。
每个server在binding中将其队列绑定到一个fanout exchange, client在调用时指定exchange类型为fanout即可。server和client使用同一个exchange。
目前Openstack中有两种RPC实现,一种是在oslo messaging,一种是在openstack.common.rpc。
openstack.common.rpc是旧的实现,oslo messaging是对openstack.common.rpc的重构。openstack.common.rpc在每个项目中都存在一份拷贝,oslo messaging即将这些公共代码抽取出来,形成一个新的项目。oslo messaging也对RPC API进行了重新设计,具体参考前文。
以后的方向是各个项目都会使用oslo messaging的RPC功能,停止使用openstack.common.rpc。目前(icehouse release)nova, cinder都已经完成转变,neutron还在使用openstack.common.rpc。
rpc.call和rpc.cast的区别:
从实现代码上看,他们的区别很小,就是call调用时候会带有wait_for_reply=True参数,cast不带
notification
oslo messaging中除了RPC外,还有另外一种跨进程通信方式,即消息通知(notification)。notification和前面的第三种RPC场景(广播系统)非常类似,区别就是notification的消息(message)格式是有固定格式的,而RPC中的消息并无固定格式,取决于client/server之间的约定。
目前消息系统的主要receiver(消息收集者)为ceilometer系统,而publisher就是Openstack个项目的service。如nova-compute会针对虚拟机的生命周期发出各种通知:start/stop/create/destroy等。
notification的底层机制可以使用RPC,及driver类型为MessagingDriver。
具体参见:https://wiki.openstack.org/wiki/Oslo/Messaging#oslo.notify