openstack组件oslo.message之RPCClient
来源:互联网 发布:图像处理算法从何学起 编辑:程序博客网 时间:2024/05/24 03:12
根据openstack官网介绍,oslo.messaging库就是把rabbitmq的python库做了封装,在openstack中调用RPC通信就要调用oslo.messaging库。
下面介绍oslo.messaging在RPC通信过程中两个重要部分,RPC Client,Server。
翻译官网Server和Client中介绍:
Server是RPC服务器提供多个端点,每个包含一组远程调用客户端的方法,创建一个PRC服务器,提供一个传输队列,目标和端点列表。
Client是一个类调用远程服务器上的方法,RPCClient类是负责发送方法调用通过消息传输到远程服务器。
在/nova/rpc.py有获取客户端的方法
def get_client(target, version_cap=None, serializer=None): assert TRANSPORT is not None serializer = RequestContextSerializer(serializer) return messaging.RPCClient(TRANSPORT, target, version_cap=version_cap, serializer=serializer)
获取客户端,通过调用messging.RPCClient()方法,再来查看RPCClient方法,这个方法不在navo的源码当中,通过上面的引用importoslo_messagingasmessaging可以看出是从oslo_messaging类中引用过来的,下面来查看oslo_messaging组件。
在代码/oslo_messaging/rpc/client.py中定义了RPCClient类
class RPCClient(object): """<span style="white-space:pre"></span>一个远程调用服务器的方法的类 """ def __init__(self, transport, target, timeout=None, version_cap=None, serializer=None, retry=None): """构造一个RPC的客户端 """ self.conf = transport.conf self.conf.register_opts(_client_opts) self.transport = transport self.target = target self.timeout = timeout self.retry = retry self.version_cap = version_cap self.serializer = serializer or msg_serializer.NoOpSerializer() super(RPCClient, self).__init__() _marker = _CallContext._marker def prepare(self, exchange=_marker, topic=_marker, namespace=_marker, version=_marker, server=_marker, fanout=_marker, timeout=_marker, version_cap=_marker, retry=_marker): """Prepare a method invocation context. """ return _CallContext._prepare(self, exchange, topic, namespace, version, server, fanout, timeout, version_cap, retry) def cast(self, ctxt, method, **kwargs): """调用一个方法并立即返回 """ self.prepare().cast(ctxt, method, **kwargs) def call(self, ctxt, method, **kwargs): """调用一个方法,等待回复 """ return self.prepare().call(ctxt, method, **kwargs) def can_send_version(self, version=_marker): """检查版本是否兼容的版本.""" return self.prepare(version=version).can_send_version()RPC消息发送函数cast,发送消息立即返回
def cast(self, ctxt, method, **kwargs): //消息组装 msg = self._make_message(ctxt, method, kwargs) ctxt = self.serializer.serialize_context(ctxt) if self.version_cap: self._check_version_cap(msg.get('version')) try: self.transport._send(self.target, ctxt, msg, retry=self.retry) except driver_base.TransportDriverError as ex: raise ClientSendError(self.target, ex)消息组装:_make_message
def _make_message(self, ctxt, method, args): msg = dict(method=method)<span style="white-space:pre"></span>//消息组装函数 method接收端函数名称 args 接收端函数参数 msg['args'] = dict() for argname, arg in six.iteritems(args): msg['args'][argname] = self.serializer.serialize_entity(ctxt, arg)<span style="white-space:pre"></span> if self.target.namespace is not None: msg['namespace'] = self.target.namespace if self.target.version is not None: msg['version'] = self.target.version return msg消息的发送调用transport._send()
def _send(self, target, ctxt, message, wait_for_reply=None, timeout=None, envelope=True, notify=False, retry=None): class Context(object): def __init__(self, d): self.d = d def to_dict(self): return self.d context = Context(ctxt) msg = message //若是需要回复,则设置msg_id等信息 if wait_for_reply: msg_id = uuid.uuid4().hex msg.update({'_msg_id': msg_id}) msg.update({'_reply_q': self._get_reply_q()}) //msg消息获取唯一的unique_id rpc_amqp._add_unique_id(msg) unique_id = msg[rpc_amqp.UNIQUE_ID] rpc_amqp.pack_context(msg, context) if envelope: msg = rpc_common.serialize_msg(msg) //若需要回复,则监听回复的msg_id if wait_for_reply: self._waiter.listen(msg_id) log_msg = "CALL msg_id: %s " % msg_id else: log_msg = "CAST unique_id: %s " % unique_id //获取一个connection的对象 try: with self._get_connection(rpc_amqp.PURPOSE_SEND) as conn: if notify: exchange = self._get_exchange(target) log_msg += "NOTIFY exchange '%(exchange)s'" \ " topic '%(topic)s'" % { 'exchange': exchange, 'topic': target.topic} LOG.debug(log_msg) conn.notify_send(exchange, target.topic, msg, retry=retry) elif target.fanout: log_msg += "FANOUT topic '%(topic)s'" % { 'topic': target.topic} LOG.debug(log_msg) conn.fanout_send(target.topic, msg, retry=retry) else: topic = target.topic exchange = self._get_exchange(target) if target.server: topic = '%s.%s' % (target.topic, target.server) log_msg += "exchange '%(exchange)s'" \ " topic '%(topic)s'" % { 'exchange': exchange, 'topic': target.topic} LOG.debug(log_msg) //初始化一个TopicPublisher的对象 conn.topic_send(exchange_name=exchange, topic=topic, msg=msg, timeout=timeout, retry=retry) if wait_for_reply: result = self._waiter.wait(msg_id, timeout) if isinstance(result, Exception): raise result return result finally: if wait_for_reply: self._waiter.unlisten(msg_id)从RabbitMQ连接池中获取一个链接:_get_connectiong()
def _get_connection(self, purpose=rpc_amqp.PURPOSE_SEND): return rpc_amqp.ConnectionContext(self._connection_pool, purpose=purpose)方法ConnectionContext的初始化
def __init__(self, connection_pool, purpose): """创建一个链接或者从一个链接池中获取一个链接""" self.connection = None self.connection_pool = connection_pool pooled = purpose == PURPOSE_SEND if pooled: self.connection = connection_pool.get() else: # a non-pooled connection is requested, so create a new connection self.connection = connection_pool.create(purpose) self.pooled = pooled self.connection.pooled = pooled
现在看下方法topic_send的实现
def topic_send(self, exchange_name, topic, msg, timeout=None, retry=None): """发送一个topic的信息.""" exchange = kombu.entity.Exchange( name=exchange_name, type='topic', durable=self.amqp_durable_queues, auto_delete=self.amqp_auto_delete) self._ensure_publishing(self._publish, exchange, msg, routing_key=topic, retry=retry)
在来看下_public方法的实现
def _publish(self, exchange, msg, routing_key=None, timeout=None): """发布消息.""" producer = kombu.messaging.Producer(exchange=exchange, channel=self.channel, routing_key=routing_key) expiration = None if timeout: expiration = int(timeout * 1000) transport_timeout = timeout heartbeat_timeout = self.heartbeat_timeout_threshold if (self._heartbeat_supported_and_enabled() and ( transport_timeout is None or transport_timeout > heartbeat_timeout)): transport_timeout = heartbeat_timeout log_info = {'msg': msg, 'who': exchange or 'default', 'key': routing_key} LOG.trace('Connection._publish: sending message %(msg)s to' ' %(who)s with routing key %(key)s', log_info) with self._transport_socket_timeout(transport_timeout): producer.publish(msg, expiration=expiration)
方法call的发送过程,和cast类似,区别在于call方法等待consumer结束要拿到返回值。call需要考虑超时和捕获异常。
def call(self, ctxt, method, **kwargs): """Invoke a method and wait for a reply. See RPCClient.call().""" if self.target.fanout: raise exceptions.InvalidTarget('A call cannot be used with fanout', self.target) //封装消息 msg = self._make_message(ctxt, method, kwargs) msg_ctxt = self.serializer.serialize_context(ctxt) //设置超时时间 timeout = self.timeout if self.timeout is None: timeout = self.conf.rpc_response_timeout if self.version_cap: self._check_version_cap(msg.get('version')) //发送 try: result = self.transport._send(self.target, msg_ctxt, msg, wait_for_reply=True, timeout=timeout, retry=self.retry) except driver_base.TransportDriverError as ex: raise ClientSendError(self.target, ex) return self.serializer.deserialize_entity(ctxt, result)
重新回到_send方法,因为等待参数生效,所以分析等待回复的队列_get_reply_q()
def _get_reply_q(self): with self._reply_q_lock: if self._reply_q is not None: return self._reply_q reply_q = 'reply_' + uuid.uuid4().hex //获取一个连接 conn = self._get_connection(rpc_amqp.PURPOSE_LISTEN) //等下分析 self._waiter = ReplyWaiter(reply_q, conn, self._allowed_remote_exmods) self._reply_q = reply_q self._reply_q_conn = conn return self._reply_q分析ReplyWaiter这个类
class ReplyWaiter(object): def __init__(self, reply_q, conn, allowed_remote_exmods): self.conn = conn self.allowed_remote_exmods = allowed_remote_exmods self.msg_id_cache = rpc_amqp._MsgIdCache() self.waiters = ReplyWaiters() //声明一个消费者 self.conn.declare_direct_consumer(reply_q, self) self._thread_exit_event = threading.Event() self._thread = threading.Thread(target=self.poll) self._thread.daemon = True self._thread.start()刚才传进来的reply_q进入了self.conn.declare_direct_consumer(reply_q,self)方法
def declare_direct_consumer(self, topic, callback): """创建一个direct类型的queue """ consumer = Consumer(exchange_name=topic, queue_name=topic, routing_key=topic, type='direct', durable=False, auto_delete=True, callback=callback, rabbit_ha_queues=self.rabbit_ha_queues) self.declare_consumer(consumer)这里可以看到这个队列是一个Direct类型的消费者,在看下declare_consumer方法的实现
def declare_consumer(self, consumer): """ 创建一个消费者类, """ def _connect_error(exc): log_info = {'topic': consumer.routing_key, 'err_str': exc} LOG.error(_LE("Failed to declare consumer for topic '%(topic)s': " "%(err_str)s"), log_info) def _declare_consumer(): consumer.declare(self) self._consumers.append(consumer) self._new_consumers.append(consumer) return consumer with self._connection_lock: return self.ensure(_declare_consumer, error_callback=_connect_error)再次重新回到_send方法,listen方法就是监听返回值
if wait_for_reply: self._waiter.listen(msg_id) log_msg = "CALL msg_id: %s " % msg_id最后面的如果返回有异常则抛出异常,最后取消监听
if wait_for_reply: result = self._waiter.wait(msg_id, timeout) if isinstance(result, Exception): raise result return result finally: if wait_for_reply: self._waiter.unlisten(msg_id)
1 0
- openstack组件oslo.message之RPCClient
- openstack组件oslo.message之RPCServer实现
- OpenStack公共组件oslo之二——oslo.utils
- OpenStack公共组件oslo之三——oslo.log
- OpenStack公共组件oslo之四——oslo.context
- OpenStack公共组件oslo之五——oslo.service
- OpenStack公共组件oslo之六——oslo.messaging
- OpenStack公共组件oslo之七——oslo.middleware
- OpenStack公共组件oslo之八——oslo.i18n
- OpenStack公共组件oslo之九——oslo.db
- OpenStack公共组件oslo之十——oslo.concurrency
- OpenStack公共组件oslo之十一——oslo.serialization
- OpenStack公共组件oslo之十二——oslo.policy
- OpenStack公共组件oslo之十三——oslo.cache
- Openstack oslo.message rpc简介
- OpenStack公共组件oslo之十四——pbr
- OpenStack公共组件oslo之十五——taskflow
- OpenStack公共组件oslo之十六——stevedore
- eclipse安装插件提示Duplicate Location错误的解决办法
- Android圆形图片--自定义控件
- JAVABEAN是什么和总结JAVABEAN的两种使用方式06-7-23
- Java生成和操作Excel文件
- 乐观锁与悲观锁 转自http://www.cnblogs.com/guyufei/archive/2011/01/10/1931632.html
- openstack组件oslo.message之RPCClient
- iOS RGB颜色跟16进制颜色转换 宏定义
- 个人笔记 js 04 把子页面把参数传递到父页面的方法
- 7个答案告诉你实时个性化营销的现状未来
- 【Android】ToolBar设置NavigationIcon不显示异常或自定义失败异常
- 安卓中为View添加动画效果(尺寸缩放、透明度渐变、旋转、移动)
- 程序员面试白皮书——互动出版网
- SendMessage与PostMessage
- centos65 folly的编译安装