在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,transport_url)
#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的監控界面。

