Openstack中幾乎所有的組件都使用MQ作爲消息傳遞的中間件,通過MQ完成了很多協程任務,實現了服務之間的分佈式部署。所以學習Openstack中的MQ模塊可以更好的掌握Openstack組件之間的通信,其中,oslo.message模塊封裝了Openstack中幾乎所有的對MQ的操作,而rpc又是MQ操作中最典型的應用場景,對它的學習很有必要。
Oslo.message 中的重要概念
server(服務器端): 服務器端爲客戶端提供了可用的PRC接口。
client(客戶端): 客戶端負責調用服務器端提供的RPC接口。
exchange(交換):一個包含了各個項目主題(topic)的容器。
topic(主題):一個RPC接口的標識,服務器端在某個主題上監控方法調用,而客戶端在某個主題上調用方法。
namespace(命名空間):服務器端可以在一個主題上暴露多組方法,而每組方法屬於一個命名空間。
method(方法):一個方法由一個名字和一組命名的參數組成。
transport(傳輸工具):一個傳送PRC請求到服務器端並將響應返回給客戶端的底層消息系統。目前常用的transport有 rabbitmq 和qpid。
API version:每個命名空間都有一個版本號,當命令空間的接口變化時,這個版本號也會相應增加。向前兼容的修改只需要更改小版本號,向前不兼容的更改需要更改大版本號。
Oslo.messsage 的典型應用場景
1. Invoke method on one of multiple servers(選擇多個服務器端的一個服務器進行遠程方法調用)
這種情況下,會有多個服務器端在一個exchange和一個topic上監聽方法調用,當客戶在相應的exchange和topic上調用方法時,這個請求將會分發給多個服務器端中的一個服務器進行方法的調用。請求的方式會採用round robin模式。
下面的代碼是這種應用場景在qpid上的實現,首先是服務器端的代碼:
#! /usr/bin/env python
import sys
import time
import logging
from oslo_config import cfg
from oslo import messaging
class TestEndpoint(object):
def __init__(self, server, target=None):
self.server = server
self.target = target
def methodA(self, ctx, **args):
print("%s::TestEndpoint::methodA( ctxt=%s arg=%s ) is called"
% (self.server, str(ctx),str(args)))
def main():
url = 'qpid://localhost:5673'
exchange = 'my-exchange'
topic = 'my-topic'
namespace = 'my-namespace'
argv = sys.argv[1:]
if argv is None:
print 'You should specify the server name'
return -1
server = argv[0]
version = '1.1'
logging.basicConfig(level=logging.INFO)
transport = messaging.get_transport(cfg.CONF, url=url)
target = messaging.Target(exchange=exchange,
topic=topic,
namespace=namespace,
server=server,
version=version)
endpoints = [TestEndpoint(server, target)]
server = messaging.get_rpc_server(transport, target, endpoints, executor='blocking')
try:
server.start()
except KeyboardInterrupt:
server.stop()
server.wait()
if __name__ == "__main__":
sys.exit(main())
接下來是是客戶端的代碼:
#! /usr/bin/env python
import sys
import time
import logging
from oslo_config import cfg
from oslo import messaging
def main(argv=None):
url = 'qpid://localhost:5673'
exchange = 'my-exchange'
topic = 'my-topic'
namespace = 'my-namespace'
version = '1.1'
logging.basicConfig(level=logging.INFO)
transport = messaging.get_transport(cfg.CONF, url=url)
target = messaging.Target(exchange=exchange,
topic=topic,
namespace=namespace,
version=version)
client = messaging.RPCClient(transport, target, version_cap=version)
test_context = {"application": "my-client",
"time": time.ctime(),
"cast": False}
args = {}
client.call(test_context, 'methodA', **args)
transport.cleanup()
if __name__ == "__main__":
sys.exit(main())
確保使用以下的方式啓動qpid和執行代碼:
qpidd --auth no -p 5673 #啓動qpid
python rpc_qpid_server.py my-server01 #啓動server01
python rpc_qpid_server.py my-server02 #啓動server02
python rpc_qpid_client.py # 發送消息
這種情況下,會有多個服務器端在一個exchange和一個topic上監聽方法調用,客戶在相應的exchange和topic上調用方法並指定具體一個server進行方法調用。
此時只需要修改客戶端中建立target的代碼:
target = messaging.Target(exchange=exchange,
topic=topic,
<strong>server='my-server01',</strong>
namespace=namespace,
version=version)
3. Invoke Method on all of Multiple Servers(在所有的服務器中進行方法調用)
此時需要採用fanout的方法,修改客戶端的代碼如下:
target = messaging.Target(exchange=exchange,
topic=topic,
<strong> fanout=True,</strong>
namespace=namespace,
version=version)
Oslo.message 提供的rpc的方式:
1. cast - 採用異步的方式,沒有結果返回
2. call - 採用同步的方式,會等待結果返回
上面所說的第三個應用場景只能採用cast的方式。
參考文獻: https://wiki.openstack.org/wiki/Oslo/Messaging