在openstack中,各个组件之间的调用遵循RESTful风格,而组件内部各服务之间的相互调用采用rpc远程调用,比如nova-conductor和nova-compute
rpc原理:
首先了解什么叫RPC,为什么要RPC,RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。比如说,一个方法可能是这样定义的:
Employee getEmployeeByName(String fullName)那么:
- 首先,要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。
- 第二,要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,如何连接到B服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么,这样才能完成调用。比如基于Web服务协议栈的RPC,就要提供一个endpoint
URI,或者是从UDDI服务上查找。如果是RMI调用的话,还需要一个RMI Registry来注册服务的地址。
- 第三,当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。
- 第四,B服务器收到请求后,需要对参数进行反序列化(序列化的逆操作),恢复为内存中的表达方式,然后找到对应的方法(寻址的一部分)进行本地调用,然后得到返回值。
- 第五,返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给A服务器上的应用
rpc的简单实现:
参考博文:https://www.jianshu.com/p/5b90a4e70783
OpenStack处理消息队列的oslo-messaging依赖包
与RabbitMQ调用相关的类和方法:
Trove在M版本中使用OpenStack提供的oslo-messaging依赖包进行RPC调用,在oslo-messaging中与RPC调用相关的类和方法主要由以下几个:
1) Target类:该类主要用于描述客户端发送的消息去哪儿和服务端接收什么消息。其主要成员变量有:
exchange:将接收到的消息分类,并告知消息分发到何种路由何种队列。
topic:是RPC消息的唯一标识,客户端发送topic的消息,服务端则接收处理对应topic的消息。
namespace:服务端可以在一个topic上提供多个方法集合,这个集合通过namespace分开管理。
fanout:如果为True,则将消息发送到所有满足条件的server上,此时会忽略topic指定的内容。
server:服务端标识。
version:标识rpc api的版本。
2) Transport类:实现监听和发送消息的抽象层,具体实现则是由Transport成员变量_driver来定义的。
3) RabbitDriver类:具体实现消息的监听,发送等操作,在OpenStack Trove组件中使用的是RabbitMQ消息队列,因此在调用时会匹配到RabbitDriver类。
4) MassageHandlingServer类:监听消息的服务端。将一个Transport和一个PRCDispatcher联系起来,用于分发和处理消息。
5) RPCDispatcher类:定义了具体的消息分发机制。
6) RPCClient类:消息客户端,利用一个可调用_CallContext对象发送消息。
OpenStack RPC处理流程:
在OpenStack使用RPC传输和处理消息时,首先会根据消息接收端的Target和endpoints初始化一个RPCDispatcher,然后会根据具体的Transport和定义的RPCDispatcher创建一个MessageHandlingServer接收并处理消息。
当实例化一个RPCClient之后,客户端发送cast或call消息,最终会调用Transport中定义的RabbitDriver对应的send()方法发送消息。
当Server端接收到消息后,则会调用RPCDispatcher对象中的dispatch()方法定义的规则找到具体的Manager处理消息中对应的操作。
如果是call调用,则会监听处理方法时候执行完成,当执行完成之后则返回相应的值。