在 openstack 中使用两种通信方式, 一种是 Restful API, 另一种是远程过程调用 RPC. 本片文章主要讲解 openstack 中 RPC 的使用方式, 以及如何在我们自己的架构中使用 RPC.
在我前面的一篇文章《基于 Rabbitmq 的 RPC 调用》中已经简单的介绍过 RPC,Rabbitmq 两种技术, openstack 中的 RPC 调用实现是自己的通用库 oslo_message, 该库是对基于 Rabbitmq 实现的 RPC 的一个封装.
一, 技术介绍
nova 模块是 openstack 中最核心的服务, nova 模块使用了众多的 RPC 服务将业务解耦, 如: nova-API; nova-conductor; nova-scheduler; nova-compute 等. 每一个服务都是一个 RPC 的服务端, 同时每一个服务都是一个 RPC 的客户端.
service 服务启动的就是 nova 的 RPC 服务
nova 各个服务启动时就是开启了一个 RPC 的服务端, 在调用其他服务时会创建一个客户端, 通过客户端调用到相应的服务. 以 nova 创建虚拟机为例, 在上图的第三步中, nova-scheduler 选择好计算节点, 将创建虚拟机的信息通过 RPC 发送给计算节点. 代码经过 RPC 客户端的封装和调用, 本文的重点就是如何调用 RPC 客户端.
- cctxt = self.client.prepare(version=version)
- cctxt.cast(context,'select_destinations',**kw)
这里的 client 是 rpc.get_client(target,serializer=serializer), 见下图.
通过查找 rpc 的导入路径, 可以发现 rpc 是从 nova 模块中导入.
rpc.py 在 get_client 函数中, 返回了 messaging 的调用.
这里的 messaging 就是来自 oslo_messaging 库.
从 rpc.py 中可知, 客户端来自 messaging, 其实就是 oslo_messaging. 所以能够看出 openstack 中 RPC 的实现封装在 oslo_messagin 库当中.
oslo_messaging 其内部的实现暂时先不管, 我们已经知道了调用其接口就能驾驭这个已经封装好的 oslo_messaging 库来完成自己的一些功能. 下面就模拟 openstack 使用 oslo_messaging 来完成自己的功能.
二, 代码移植
要使用 oslo_messagin 封装好的 RPC, 主要步骤分为如下几步:
安装 oslo_messaging 库
安装 rabbitmq 消息队列
创建消息队列用户名
设置用户权限
创建配置文件
调用 oslo_messaging 中客户端, 服务器端
启动服务
1, 安装 oslo_messaging 库
pip install oslo_messaging oslo_config
2, 安装 rabbitmq
apt-get install rabbitmq-server
3, 创建消息队列用户名
rabbitmqctl add_user openstack stack2018
4, 设置权限
rabbitmqctl set_permissions openstack ".*" ".*" ".*"
其中步骤 2, 步骤 3, 步骤 4 对很对读者来说肯定十分眼熟, 不错, 流程和 openstack 安装 Message queue 是一样一样的.
5, 创建配置文件
- my.conf
- [DEFAULT]
- url = 'rabbit://openstack:stack2018@127.0.0.1:5672/'
用户名: openstack , 密码: stack2018
6, 调用 oslo_messaging, 完成客户端和服务器端
oslo_message_server.py
- #coding:utf-8
- from oslo_config import cfg
- import oslo_messaging
- import time
- class ServerControlEndpoint(object):
- def __init__(self, server):
- self.server = server
- def stop(self, ctx):
- if self.server:
- self.server.stop()
- class AddEndpoint(object):
- def add(self, ctx, a,b):
- print 'revice message'
- return a+b
- # 从配置文件中加载 transport_url. 在 openstack 中, 账号, 密码, 端口号等都是从配置文件中读取, 支持可配置的.
- # 配置文件的内容通过 oslo_config 库读取.
- opts = [
- cfg.StrOpt('url', default='0.0.0.0'),
- ]
- CONF = cfg.CONF
- CONF.register_opts(opts)
- CONF(default_config_files=['my.conf'])
- #transport_url 是指定实现 RPC 的底层技术, 可以使 rabbitmq, 也可以是别的技术
- # 从 my.conf 文件中读取到该 URL.
- transport_url = CONF.url
- transport = oslo_messaging.get_transport(cfg.CONF)
- #target 用来指定该 rpc server 监听在哪些队列上.
- #target 指定了 2 个参数: topic 和 server.
- target = oslo_messaging.Target(topic='test', server='server1')
- # 可供别人调用的方法类
- endpoints = [
- ServerControlEndpoint(None),
- AddEndpoint(),
- ]
- # 创建 Server 对象时, 需要指定 Transport,Target 和一组 endpoint
- server = oslo_messaging.get_rpc_server(transport, target, endpoints,
- executor='blocking')
- try:
- server.start()
- print 'The Server!'
- while True:
- time.sleep(1)
- except KeyboardInterrupt:
- print("Stopping server")
- server.stop()
service 文件中使用两个 openstack 通用库, 除了 oslo_messaging 之外, 还有一个 oslo_config. 首先说 oslo_config, 这个库的主要功能是从配置文件或者命令行中读取特定信息. 在 openstack 的安装过程中, 需要配置各种参数, 例如 nova.conf 中配置 rabbitmq.
同样, 我们在配置文件中也配置了 transport_url, 通过 oslo_config 读取配置的值. 下面的 client 也是一样, 通过 oslo_config 读取配置信息. 另外一个就是主角 oslo_messaging 的调用. 创建一个 RPC 的服务端, 需要四个参数, 分别是:
- transport
- target
- endpoints
- executor
他们的功能分别是:
transport RPC 功能的底层实现方法, 这里是 rabbitmq 的消息队列的访问路径
target 指定 RPC topic 交换机的匹配信息和绑定主机
endpoints 是可供别人远程调用的方法, 也是 RPC 中的远程函数
executor 服务的运行方式, 单线程或者多线程
通过这四个参数, 形成一个可调用的 RPC 服务端, 服务以阻塞的方式在后台运行.
oslo_message_client.py
- #coding:utf-8
- from oslo_config import cfg
- import oslo_messaging
- opts = [
- cfg.StrOpt('url', default='helloworld'),
- ]
- CONF = cfg.CONF
- CONF.register_opts(opts)
- CONF(default_config_files=['my.conf'])
- transport_url = CONF.url
- transport = oslo_messaging.get_transport(cfg.CONF,transport_url)
- target = oslo_messaging.Target(topic='test')
- client = oslo_messaging.RPCClient(transport, target)
- r = client.call({}, 'add', a=2,b=3)
- print 'result :',r
- #Target 对象的属性在 RPCClient 对象构造之后, 还可以通过 prepare() 方法修改,
- # 可以修改的属性包括 exchange,topic,namespace,version,server,fanout 和 retry.
- # 修改后的 target 属性只在这个 prepare() 方法返回的对象中有效.
- cctxt = client.prepare(version='1.0')
- r_two = cctxt.call({},'add',a=2,b=3)
- print 'result_two :',r_two
客户端调用了 oslo_messaging.RPCClient() 方法, 这个方式就是 openstack 调用 oslo_messaging 库的方法. 通过传入参数: transport: 消息处理的端点; target: 消息队列中 topic 的路由关键字, 能够准确定位到要发送的消息队列的. 另外如果创建的 client 客户端是共用的, 而具体使用时还要修改自己的特性的话, 可以使用 client.prepare 这个方法来修改 client 的参数. 例如代码中使用该方法修改了 client 的版本号. 这样和 openstack 的代码就是保持了一致.
7, 开启服务
先开启服务器
运行客户端, 调用远程函数
一次 RPC 的远程调用就完成了, 同时这也是贴近 openstack 源码的 RPC 使用方式, 很方便在自己的架构中移植这种现有的技术.
最后还有一个小彩蛋. 在安装 rabbitmq 的主机上, 进入路径 cd /usr/lib/rabbitmq/bin/, 然后执行命令
rabbitmq-plugins enable rabbitmq_management
在浏览器中进入地址 http://localhost:15672, 使用账号 guest/guest 登录, 能打开 rabbitmq 的监控界面.
来源: https://www.cnblogs.com/goldsunshine/p/10205058.html