这一章主要讲openstack的组件oslo.message中RPCServer实现
在openstack中初始化一个RPC的server源代码:在/nova/rpc.py中
def get_server(target, endpoints, serializer=None):
assert TRANSPORT is not None
serializer = RequestContextSerializer(serializer)
return messaging.get_rpc_server(TRANSPORT,
target,
endpoints,
executor='eventlet',
serializer=serializer)
首先看看messaging.get_rpc_server,文件位于oslo_messaging/rpc/server.py中
def get_rpc_server(transport, target, endpoints,
executor='blocking', serializer=None):
"""rpc_server代码结构
"""
dispatcher = rpc_dispatcher.RPCDispatcher(target, endpoints, serializer)
return msg_server.MessageHandlingServer(transport, dispatcher, executor)
exector参数控制如何接收和派遣传入消息,关于transport,dispatch,executor三者区别,引用一段代码注释Connect a transport to a dispatcher that knows how to process the message using an executor that knows how the app wants to create new tasks.transport是传输层,在消息中间件层接收消息,executor确定接收消息的线程模型,dispatch负责最终处理消息。
下面看下第一个类RPCDispatcher,文件位于oslo_messaging/rpc/dispatcher.py
class RPCDispatcher(object):
"""在PRC消息之下的消息调度
"""
def __init__(self, target, endpoints, serializer):
"""rpc服务器调度的结构
"""
self.endpoints = endpoints
self.serializer = serializer or msg_serializer.NoOpSerializer()
self._default_target = msg_target.Target()
self._target = target
回到get_rpc_server方法,在分析下MessageHandlingServer类的初始化
class MessageHandlingServer(service.ServiceBase):
"""消息处理服务器
"""
def __init__(self, transport, dispatcher, executor='blocking'):
"""消息处理服务器的结构
"""
self.conf = transport.conf
self.transport = transport
self.dispatcher = dispatcher
self.executor = executor
//executor如果是blocking,则在新的线程处理消息,其他的则在当前线程处理消息
if self.executor != "blocking":
self._state_cond = threading.Condition()
self._dummy_cond = False
else:
self._state_cond = _utils.DummyCondition()
self._dummy_cond = True
try:
mgr = driver.DriverManager('oslo.messaging.executors',
self.executor)
except RuntimeError as ex:
raise ExecutorLoadFailure(self.executor, ex)
else:
self._executor_cls = mgr.driver
self._executor = None
self._running = False
super(MessageHandlingServer, self).__init__()
dispacher的参数是一个可调用的上下文和收到的消息字典,执行程序参数控制如何接收和派遣传入消息
在回到刚开始,rpcserver.start()方法的实现,rpc.get_server()返回的MessageHandingServer,所以start()方法也就是MessageHandlingServer的start方法
def start(self):
"""start方法处理传递过来的信息
"""
if self._executor is not None:
return
with self._state_cond:
if self._executor is not None:
return
try:
listener = self.dispatcher._listen(self.transport)
except driver_base.TransportDriverError as ex:
raise ServerListenError(self.target, ex)
self._running = True
self._executor = self._executor_cls(self.conf, listener,
self.dispatcher)
self._executor.start()
self._state_cond.notify_all()
这种方法使服务器开始为传入的消息,通过轮询传输调度程序。消息直到调用stop()方法停止。下面来一句一句的分析
listener = self.dispatcher._listen(self.transport)
这里调用的_listen是的transport类中的方法_listen
def _listen(self, target):
if not (target.topic and target.server):
raise exceptions.InvalidTarget('A server\'s target must have '
'topic and server names specified',
target)
return self._driver.listen(target)
如果没有指定target.topic和target.server则报错,必须要指定,下面看下listen的源码
def listen(self, target):
conn = self._get_connection(rpc_amqp.PURPOSE_LISTEN)
listener = AMQPListener(self, conn)
conn.declare_topic_consumer(exchange_name=self._get_exchange(target),
topic=target.topic,
callback=listener)
conn.declare_topic_consumer(exchange_name=self._get_exchange(target),
topic='%s.%s' % (target.topic,
target.server),
callback=listener)
conn.declare_fanout_consumer(target.topic, listener)
return listener
首先获取一个连接,然后建立一个Listen对象
class AMQPListener(base.Listener):
//AMQPListener的初始化
def __init__(self, driver, conn):
super(AMQPListener, self).__init__(driver)
self.conn = conn
self.msg_id_cache = rpc_amqp._MsgIdCache()
self.incoming = []
self._stopped = threading.Event()
self._obsolete_reply_queues = ObsoleteReplyQueuesCache()
后来就是声明三个consumer,首先看看declare_topic_consumer()
def declare_topic_consumer(self, exchange_name, topic, callback=None,
queue_name=None):
"""创建一个Topic的消费者."""
consumer = Consumer(exchange_name=exchange_name,
queue_name=queue_name or topic,
routing_key=topic,
type='topic',
durable=self.amqp_durable_queues,
auto_delete=self.amqp_auto_delete,
callback=callback,
rabbit_ha_queues=self.rabbit_ha_queues)
self.declare_consumer(consumer)
declare_topic_consumer()函数主要是封装一个Topic的消费者,然后看下Consumer的类
class Consumer(object):
"""Consumer类"""
def __init__(self, exchange_name, queue_name, routing_key, type, durable,
auto_delete, callback, nowait=True, rabbit_ha_queues=None):
"""初始化Consumer类 exchange_name, routing_key,type, durable auto_delete等参数
"""
self.queue_name = queue_name
self.exchange_name = exchange_name
self.routing_key = routing_key
self.auto_delete = auto_delete
self.durable = durable
self.callback = callback
self.type = type
self.nowait = nowait
self.queue_arguments = _get_queue_arguments(rabbit_ha_queues)
self.queue = None
self.exchange = kombu.entity.Exchange(
name=exchange_name,
type=type,
durable=self.durable,
auto_delete=self.auto_delete)
同样declare_fanout_consumer()方法实现和topic一样
def declare_fanout_consumer(self, topic, callback):
"""创建一个fanout的消费者."""
unique = uuid.uuid4().hex
exchange_name = '%s_fanout' % topic
queue_name = '%s_fanout_%s' % (topic, unique)
consumer = Consumer(exchange_name=exchange_name,
queue_name=queue_name,
routing_key=topic,
type='fanout',
durable=False,
auto_delete=True,
callback=callback,
rabbit_ha_queues=self.rabbit_ha_queues)
self.declare_consumer(consumer)
继续回到前面看MessageHandingServer的start方法,现在已经将消息相关的exchange、queue都建立好了
self._executor = self._executor_cls(self.conf, listener,
self.dispatcher)
self._executor.start()
开始调用blocking的start方法,执行监听过程
def start(self):
self._running = True
while self._running:
with self.dispatcher(self.listener.poll()) as callback:
callback()
关键进入self.dispatcher()的callbac()方法
def __call__(self, incoming):
incoming.acknowledge()
yield lambda: self._dispatch_and_reply(incoming)
第二行可以看到这里对消息做了一个ACK操作,当从incoming中取出后就要做ACK,执行完之后就要进行调度和回复具体看方法_dispatch_and_reply
def _dispatch_and_reply(self, incoming, executor_callback):
try:
incoming.reply(self._dispatch(incoming.ctxt,
incoming.message,
executor_callback))
except ExpectedException as e:
LOG.debug(u'Expected exception during message handling (%s)',
e.exc_info[1])
incoming.reply(failure=e.exc_info, log_failure=False)
except Exception as e:
exc_info = sys.exc_info()
LOG.error(_LE('Exception during message handling: %s'), e,
exc_info=exc_info)
incoming.reply(failure=exc_info)
del exc_info
再分析_dispatch方法
def _dispatch(self, ctxt, message, executor_callback=None):
"""将RPC消息发送到一个端点的方法
"""
method = message.get('method')
args = message.get('args', {})
namespace = message.get('namespace')
version = message.get('version', '1.0')
found_compatible = False
for endpoint in self.endpoints:
target = getattr(endpoint, 'target', None)
if not target:
target = self._default_target
if not (self._is_namespace(target, namespace) and
self._is_compatible(target, version)):
continue
if hasattr(endpoint, method):
localcontext._set_local_context(ctxt)
try:
return self._do_dispatch(endpoint, method, ctxt, args,
executor_callback)
finally:
localcontext._clear_local_context()
found_compatible = True
if found_compatible:
raise NoSuchMethod(method)
else:
raise UnsupportedVersion(version, method=method)
这个方法首先做message到端点的映射,然后从消息中得到相关的method和一些参数信息。接下了就是遍历端点匹配target中的信息和message中的是否一致,如果匹配上则调用_do_dispatch方法。
def _do_dispatch(self, endpoint, method, ctxt, args, executor_callback):
ctxt = self.serializer.deserialize_context(ctxt)
new_args = dict()
for argname, arg in six.iteritems(args):
new_args[argname] = self.serializer.deserialize_entity(ctxt, arg)
func = getattr(endpoint, method)
if executor_callback:
result = executor_callback(func, ctxt, **new_args)
else:
result = func(ctxt, **new_args)
return self.serializer.serialize_entity(ctxt, result)