nova中的虚拟机调度采用RPC机制,现在分析一下这部分代码的实现。
self.compute_task_api.schedule_and_build_instances函数的定义在nova/conductor/api.py中的类ComputeTaskAPI中,代码如下:
def schedule_and_build_instances(self, context, build_requests,
request_spec, image,
admin_password, injected_files,
requested_networks, block_device_mapping,
tags=None):
self.conductor_compute_rpcapi.schedule_and_build_instances(
context, build_requests, request_spec, image,
admin_password, injected_files, requested_networks,
block_device_mapping, tags)
直接调用self.conductor_compute_rpcapi.schedule_and_build_instances函数,该函数在文件nova/conductor/rpcapi.py中的ComputeTaskAPI类中定义,代码如下:
def schedule_and_build_instances(self, context, build_requests,
request_specs,
image, admin_password, injected_files,
requested_networks,
block_device_mapping,
tags=None):
version = '1.17'
kw = {'build_requests': build_requests,
'request_specs': request_specs,
'image': jsonutils.to_primitive(image),
'admin_password': admin_password,
'injected_files': injected_files,
'requested_networks': requested_networks,
'block_device_mapping': block_device_mapping,
'tags': tags}
if not self.client.can_send_version(version):
version = '1.16'
del kw['tags']
cctxt = self.client.prepare(version=version)
cctxt.cast(context, 'schedule_and_build_instances', **kw)
在创建ComputeTaskAPI类对象的时候,会执行以下代码:
def __init__(self):
super(ComputeTaskAPI, self).__init__()
target = messaging.Target(topic=CONF.conductor.topic,
namespace='compute_task',
version='1.0')
serializer = objects_base.NovaObjectSerializer()
self.client = rpc.get_client(target, serializer=serializer)
类Target在文件./oslo_messaging/target.py中定义,包含的数据项如下所示,通过topic,namespace,version,server等确定了一对通信的客户端和服务端。
self.exchange = exchange
self.topic = topic
self.namespace = namespace
self.version = version
self.server = server
self.fanout = fanout
self.accepted_namespaces = [namespace] + (legacy_namespaces or [])
rpc.get_client()的定义在./nova/rpc.py文件中,代码如下,通过调用./oslo_messaging/rpc/client.py文件中类RPCClient中的构造函数,获取一个RPCClient类的对象。
def get_client(target, version_cap=None, serializer=None):
assert TRANSPORT is not None
if profiler:
serializer = ProfilerRequestContextSerializer(serializer)
else:
serializer = RequestContextSerializer(serializer)
return messaging.RPCClient(TRANSPORT,
target,
version_cap=version_cap,
serializer=serializer)
cctxt = self.client.prepare(version=version)函数最终调用./oslo_messaging/rpc/client.py文件中类_CallContext中的_prepare()函数,做准备工作,cctxt.cast(context, 'schedule_and_build_instances', **kw)函数最终调用./oslo_messaging/rpc/client.py文件中类_BaseCallContext中的cast()函数,代码如下:
def cast(self, ctxt, method, **kwargs):
"""Invoke a method and return immediately. See RPCClient.cast()."""
msg = self._make_message(ctxt, method, kwargs)
msg_ctxt = self.serializer.serialize_context(ctxt)
self._check_version_cap(msg.get('version'))
try:
self.transport._send(self.target, msg_ctxt, msg, retry=self.retry)
except driver_base.TransportDriverError as ex:
raise ClientSendError(self.target, ex)
调用self.transport._send()函数将调用的函数名和参数发送给服务端。transport为在文件./nova/rpc.py中定义的变量TRANSPORT,类Transport的定义在./oslo_messaging/transport.py文件中,包含conf和_driver两个变量,配置文件默认定义的dirriver为rabbit,self.transport._send()函数调用rabbit的_send()函数发送请求。