Openstack Nova(十)----Instance 创建(RPC基础 )

本文深入探讨Openstack Nova中Instance创建时的RPC机制,讲解RPC的基础概念,以及Openstack如何通过AMQP协议进行内部通信。重点阐述发送与接收过程,包括RPCClient、RpcProxy的使用,ConnectionContext、TopicPublisher的角色,以及Conductor服务的启动和Consumer协程的工作方式。
摘要由CSDN通过智能技术生成

在前一章中, 学习了Computer API及Conductor调用RPC做下一步处理。那么, 这里就有个问题, Openstack中RPC是怎么工作的?

想要知道RPC是怎么工作的, 那么第一个要了解的是什么是RPC?

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

从Openstack的文档中可以看到,Openstack的内部消息是基于AMQP协议的。 而且从代码结构上看, 它支持Kombu, Qpid, ZMQ三种类型。在系统中, 现在使用的是Qpid。

发送

使用方法

就以前一章的Conductor为例, 它是一个RPC的使用者。

def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,
            security_groups, block_device_mapping, legacy_bdm=True):
        instances_p = [jsonutils.to_primitive(inst) for inst in instances]
        image_p = jsonutils.to_primitive(image)
        cctxt = self.client.prepare(version='1.5')
        cctxt.cast(context, 'build_instances',
                   instances=instances_p, image=image_p,
                   filter_properties=filter_properties,
                   admin_password=admin_password,
                   injected_files=injected_files,
                   requested_networks=requested_networks,
                   security_groups=security_groups,
                   block_device_mapping=block_device_mapping,
                   legacy_bdm=legacy_bdm)

这是RPC的基本使用方法,直接使用cctxt.cast来发送消息。而cctxt是从调用client的prepare方法返回的。那么client又是怎么来的呢?

RPCClient

看Conductorr 的\__init__方法。

class ConductorAPI(rpcclient.RpcProxy):
    def __init__(self):
            version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.conductor,
                                                   CONF.upgrade_levels.conductor)
            super(ConductorAPI, self).__init__(
                topic=CONF.conductor.topic,
                default_version=self.BASE_RPC_API_VERSION,
                serializer=objects_base.NovaObjectSerializer(),
                version_cap=version_cap)
            self.client = self.get_client()
class RpcProxy(proxy.RpcProxy):

    def get_client(self, namespace=None, server_params=None):
        return RPCClient(self,
                         namespace=namespace,
                         server_params=server_params)

从这里可以看出, client是RPCClient的实例。
再看看RPCClient的实现, 这里代码不长,就全放在下面。

from nova.openstack.common.rpc import proxy


class RPCClient(object):

    def __init__(self, proxy, namespace=None, server_params=None):
        super(RPCClient, self).__init__()
        #RPC的proxy, 相当于client的lib
        self.proxy = proxy
        self.namespace = namespace
        self.server_params = server_params
        self.kwargs = {}
        self.fanout = None

    def prepare(self, **kwargs):
        # Clone ourselves
        ret = self.__class__(self.proxy, self.namespace, self.server_params)
        ret.kwargs.update(self.kwargs)
        ret.fanout = self.fanout

        # Update according to supplied kwargs
        ret.kwargs.update(kwargs)
        server = ret.kwargs.pop('server', None)
        if server:
            ret.kwargs['topic'] = '%s.%s' % (self.proxy.topic, server)
        fanout = ret.kwargs.pop('fanout', None)
        if fanout:
            ret.fanout = True

        return ret

    def _invoke(self, cast_or_call, ctxt, method, **kwargs):
        try:
            msg = self.proxy.make_namespaced_msg(method,
                                                 self.namespace,
                                                 **kwargs)
            return cast_or_call(ctxt, msg, **self.kwargs)
        finally:
            self.kwargs = {}
            self.fanout = None

    def cast(self, ctxt, method, **kwargs):
        if self.server_params:
            def cast_to_server(ctxt, msg, **kwargs):
                if self.fanout:
                    return self.proxy.fanout_cast_to_server(
                        ctxt, self.server_params, msg, **kwargs)
                else:
                    return self.proxy.cast_to_server(
                        ctxt, self.server_params, msg, **kwargs)

            caster = cast_to_server
        else:
            caster = self.proxy.fanout_cast if self.fanout else self.proxy.cast

        self._invoke(caster, ctxt, method, **kwargs)

    def call(self, ctxt, method, **kwargs):
        return self._invoke(self.proxy.call, ctxt, method, **kwargs)

    def can_send_version(self, version):
        return self.proxy.can_send_version(version)

代码不复杂, 最终的调用还是proxy, 而ConductorAPI本身就是RpcProxy的子类, 现在看看RpcProxy类的实现。

RpcProxy

from nova.openstack.common import rpc
from nova.openstack.common.rpc import common as rpc_common
from nova.openstack.common.rpc import serializer as rpc_serializer


class RpcProxy(object):
    """A helper class for rpc clients.

    This class is a wrapper around the RPC client API.  It allows you to
    specify the topic and API version in a single place.  This is intended to
    be used as a base class for a class that implements the client side of an
    rpc API.
    """

    # The default namespace, which can be overriden in a subclass.
    RPC_API_NAMESPACE = None

    def __init__(self, topic, default_version, version_cap=None,
                 serializer=None):
        """Initialize an RpcProxy.

        :param topic: The topic to use for all messages.
        :param default_version: The default API version to request in all
               outgoing messages.  This can be overridden on a per-message
               basis.
        :param version_cap: Optionally cap the maximum version used for sent
               messages.
        :param serializer: Optionaly (de-)serialize entities with a
               provided helper.
        """
        self.topic = topic
        self.default_version = default_version
        self.version_cap = version_cap
        if serializer is None:
            serializer = rpc_serializer.NoOpSerializer()
        self.serializer = serializer
        super(RpcProxy, self).__init__()

    def _set_version(self, msg, vers):
        """Helper method to set the version in a message.

        :param msg: The message having a version added to it.
        :param vers: The version number to add to the message.
        """
        v = vers if vers else self.default_version
        if (self.version_cap and not
                rpc_common.version_is_compatible(self.version_cap, v)):
            raise rpc_common.RpcVersionCapError(version_cap=self.version_cap)
        msg['version'] = v

    def _get_topic(self, topic):
        """Return the topic to use for a message."""
        return topic if topic else self.topic

    def can_send_version(self, version):
        """Check to se
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值