OpenStack之RPC调用(二)

来源:互联网 发布:urp 数据库更改 编辑:程序博客网 时间:2024/06/11 01:37

我们接着上篇博文,下面来分析一下客户端

4. 客户端:向主题服务器发送RPC请求

主题消费者创建完成后,客户端就可以向RabbitMQ服务器发送RPC请求了。还是先来看一下client.py

clinet.py

import rpcTOPIC = 'sendout_request'#消息体msg = {'method': 'add',       'args':{'v1':2, 'v2':3}}rval = rpc.call(TOPIC, msg)  #发送rpc.call请求print('Succeed implementing RPC call. the return value is %d.\n' % rval)
可以看到客户端最终会调用impl_kombu.py中的call方法,下面一起来看一下这个方法

impl_kombu.py

def call(topic, msg, timeout):    print('Making synchronous call on %s ...\n' % topic)    msg_id = DIRECT + str(uuid.uuid4())  #构造消息ID    msg.update({'msg_id': msg_id})  #将消息ID加入消息体中    print('MSG_ID is %s\n' % msg_id)    conn = rpc.create_connection()    wait_msg = CallWaiter(conn)    conn.declare_direct_consumer(msg_id, wait_msg)    conn.topic_send(topic, msg)    return wait_msg.wait_reply()

上面的代码首先创建了与RabbitMQ服务器的连接,建立过程之前已经讨论过。然后依次实现了创建直接消费者和向主题交换器发送RPC请求,其中直接消费者用于接收服务器端的响应信息,其创建过程与上篇博文中分析的主题消费者的创建过程类似,这里就不展开讨论了。我们重点来看一下如何向主题交换器发送RPC请求吧,是通过Connection类中的topic_send方法实现的。

impl_kombu.py

class Connection(object):    def topic_send(self, topic, msg):        self.publisher_send(TopicPublisher, topic, msg)    def publisher_send(self, cls, topic, msg, **kwargs):        def _publish():            publisher = cls(self.channel, topic, **kwargs)            publisher.send(msg)        self.ensure(_publish, topic)

topic_send方法调用了publisher_send方法,可以看到_publish这个内部方法中,对传入的TopicPublisher类进行了实例化操作,然后调用其send方法发送RPC消息。先来看看TopicPublisher这个类。

impl_kombu.py

class TopicPublisher(Publisher):    def __init__(self, channel, topic, **kwargs):        options = {'durable': False,                   'auto_delete': False,                   'exclusive': False}        options.update(kwargs)        super(TopicPublisher, self).__init__(channel, topic, topic,                                             type='topic', **options)
从上面的代码看出,TopicPublisher类的方法都是在其父类Publisher中完成的。接下来看看Publisher这个类。

impl_kombu.py

class Publisher(object):    def __init__(self, channel, exchange_name, routing_key, **kwargs):        self.exchange_name = exchange_name        self.routing_key = routing_key        self.type = kwargs.pop('type')        self.kwargs = kwargs        self.reconnect(channel)    def reconnect(self, channel):        self.exchange = kombu.entity.Exchange(self.exchange_name,                                              self.type,                                              **self.kwargs)        self.producer = kombu.messaging.Producer(channel,                                                 exchange=self.exchange)    def send(self, msg):        self.producer.publish(msg,routing_key=self.routing_key)
Publisher类的初始化方法中调用了reconnect方法创建了交换器和生产者对象,其中交换器的名称就是topic的值。

5. 服务器端:接收和处理RPC请求

还记得上篇博文中我们留了一个尾巴吗?服务器端最后调用的是ConsumerBase类中的consume方法中的内部方法_callback来等待并处理RPC消息请求的。下面回忆一下这个内部方法都做了些什么吧。

def _callback(raw_message):    message = self.channel.message_to_python(raw_message)          try:              msg = message.payload  #获取消息体              self.callback(msg)  #处理消息              message.ack()  #通知交换器消息处理完毕
其中callback为传递进来的ProxyCallback对象。来看ProxyCallback类,这个类的定义在rpc_amqp.py中。需要注意的是,传递过来的ProxyCallback类内部还封装了RpcDispatch类。

rpc_amqp.py

class ProxyCallback(object):    def __init__(self, proxy):        self.proxy = proxy    def __call__(self, message_data):        method = message_data.get('method')  #获取RPC调用的方法        args = message_data.get('args', {})  #获取参数列表        msg_id = message_data.get('msg_id')  #获取消息的ID        print('Receive RPC request. method is %s.\n' % method)        self._process_data(msg_id, method, args)  #处理消息    def _process_data(self, msg_id, method, args):        rval = self.proxy.dispatch(method, **args)        msg_reply(msg_id, rval)
其主要功能都定义在__call__方法中,可以看到在该方法中进行了一系列的消息解析过程,最后交给_process_data方法。_process_data方法中调用了RpcDispatch类中的dispatch方法。来看下RpcDispatch这个类吧。这个类定义在dispatch.py中。需要注意的是,与ProxyCallback类类似,这个类内部还封装了Manager类。

dispatch.py

class RpcDispatcher(object):    def __init__(self, callback):        self.callback = callback    def dispatch(self, method, **kwargs):        if hasattr(self.callback, method):            return getattr(self.callback, method)(**kwargs)        print('No such RPC method: %s\n' % method)
这里的dispatch方法通过getattr方法获取并返回了Manager类中的“method”的执行结果,这里的“method”为客户端传递进来的“add”方法。

得到相应返回结果后,我们回到上面ProxyCallback中_process_data方法,这个方法还剩下最后一句,来看msg_reply方法。这个方法同样定义在rpc_amqp.py中。

rpc_amqp.py

def msg_reply(msg_id, reply):    msg = {'result': reply}    conn = impl_kombu.Connection()    conn.direct_send(msg_id, msg)
这个方法首先创建与RabbitMQ服务器的连接,然后调用其direct_send方法来返回RPC的调用结果,该方法与上述的topic_send方法类似,这里也不再展开分析了。只需要注意较topic_send方法,direct_send方法传递的参数为msg_id,而并非topic。RabbitMQ服务器会根据这个消息的唯一标识开辟一条专用通道来回传服务器端的响应结果。

6. 客户端:创建和激活直接消费者

回到文章最开始的部分的rpc.call方法,前面分析了客户端通过topic_send向主题消费者发送RPC请求。接下来客户端还要等待接收服务器端的响应消息,这个过程是由客户端创建的直接消费者完成的,继续来看rpc.call后续的代码。

代码中通过Connection类的declare_direct_consumer方法声明创建了直接消费者,声明创建过程和declare_topic_consumer类似。主要来看CallWrite这个类。

class CallWaiter(object):    def __init__(self, connection, timeout=None):        self._connection = connection        self._result = None    def __call__(self, data):        if data['result']:            self._result = data['result']    def wait_reply(self):        self._connection.consume()        self._connection.drain_events()        return self._result
__call__方法接收服务器端返回的消息,wait_relpy方法等待RPC响应。

至此整个一个rpc.call模式分析完了,打完收工

要说明的是OpenStack中采用了多线程的方式,我们给出的实例并没有涉及,有兴趣的朋友可以去读下OpenStack的源码。



1 0
原创粉丝点击