目标:
弄清楚oslo messaging中executor为threading的处理过程
1 总入口
ceilometer/collector.py
class CollectorService(cotyledon.Service):
"""Listener for the collector service."""
def __init__(self, worker_id):
super(CollectorService, self).__init__(worker_id)
# ensure dispatcher is configured before starting other services
dispatcher_managers = dispatcher.load_dispatcher_manager()
(self.meter_manager, self.event_manager) = dispatcher_managers
self.sample_listener = None
self.event_listener = None
self.udp_thread = None
def run(self):
if cfg.CONF.collector.udp_address:
self.udp_thread = utils.spawn_thread(self.start_udp)
transport = messaging.get_transport(optional=True)
if transport:
if list(self.meter_manager):
sample_target = oslo_messaging.Target(
topic=cfg.CONF.publisher_notifier.metering_topic)
self.sample_listener = (
messaging.get_batch_notification_listener(
transport, [sample_target],
[SampleEndpoint(cfg.CONF.publisher.telemetry_secret,
self.meter_manager)],
allow_requeue=True,
batch_size=cfg.CONF.collector.batch_size,
batch_timeout=cfg.CONF.collector.batch_timeout))
self.sample_listener.start()
if list(self.event_manager):
event_target = oslo_messaging.Target(
topic=cfg.CONF.publisher_notifier.event_topic)
self.event_listener = (
messaging.get_batch_notification_listener(
transport, [event_target],
[EventEndpoint(cfg.CONF.publisher.telemetry_secret,
self.event_manager)],
allow_requeue=True,
batch_size=cfg.CONF.collector.batch_size,
batch_timeout=cfg.CONF.collector.batch_timeout))
self.event_listener.start()
分析:
1.1 关键是
sample_target = oslo_messaging.Target(
topic=cfg.CONF.publisher_notifier.metering_topic)
self.sample_listener = (
messaging.get_batch_notification_listener(
transport, [sample_target],
[SampleEndpoint(cfg.CONF.publisher.telemetry_secret,
self.meter_manager)],
allow_requeue=True,
batch_size=cfg.CONF.collector.batch_size,
batch_timeout=cfg.CONF.collector.batch_timeout))
self.sample_listener.start()
1.2 分析 get_batch_notification_listener方法
ceilometer/messaging.py
def get_batch_notification_listener(transport, targets, endpoints,
allow_requeue=False,
batch_size=1, batch_timeout=None):
"""Return a configured oslo_messaging notification listener."""
return oslo_messaging.get_batch_notification_listener(
transport, targets, endpoints, executor='threading',
allow_requeue=allow_requeue,
batch_size=batch_size, batch_timeout=batch_timeout)
分析:
1.2.1 调用
/usr/lib/python2.7/site-packages/oslo_messaging/notify/listener.py
def get_batch_notification_listener(transport, targets, endpoints,
executor='blocking', serializer=None,
allow_requeue=False, pool=None,
batch_size=None, batch_timeout=None):
"""Construct a batch notification listener
The executor parameter controls how incoming messages will be received and
dispatched.
If the eventlet executor is used, the threading and time library need to be
monkeypatched.
:param transport: the messaging transport
:type transport: Transport
:param targets: the exchanges and topics to listen on
:type targets: list of Target
:param endpoints: a list of endpoint objects
:type endpoints: list
:param executor: name of message executor - available values are
'eventlet' and 'threading'
:type executor: str
:param serializer: an optional entity serializer
:type serializer: Serializer
:param allow_requeue: whether NotificationResult.REQUEUE support is needed
:type allow_requeue: bool
:param pool: the pool name
:type pool: str
:param batch_size: number of messages to wait before calling
endpoints callacks
:type batch_size: int
:param batch_timeout: number of seconds to wait before calling
endpoints callacks
:type batch_timeout: int
:raises: NotImplementedError
"""
dispatcher = notify_dispatcher.BatchNotificationDispatcher(
endpoints, serializer)
return BatchNotificationServer(
transport, targets, dispatcher, executor, allow_requeue, pool,
batch_size, batch_timeout
)
1.2.2 调用
class BatchNotificationServer(NotificationServerBase):
def _process_incoming(self, incoming):
try:
not_processed_messages = self.dispatcher.dispatch(incoming)
except Exception:
not_processed_messages = set(incoming)
LOG.exception(_LE('Exception during messages handling.'))
for m in incoming:
try:
if m in not_processed_messages and self._allow_requeue:
m.requeue()
else:
m.acknowledge()
except Exception:
LOG.exception(_LE("Fail to ack/requeue message."))
分析:
1) self.dispatcher.dispatch(incoming)
调用/usr/lib/python2.7/site-packages/oslo_messaging/notify/dispatcher.py
self.dispatcher是BatchNotificationDispatcher对象
class BatchNotificationDispatcher(NotificationDispatcher):
"""A message dispatcher which understands Notification messages.
A MessageHandlingServer is constructed by passing a callable dispatcher
which is invoked with a list of message dictionaries each time 'batch_size'
messages are received or 'batch_timeout' seconds is reached.
"""
def dispatch(self, incoming):
"""Dispatch notification messages to the appropriate endpoint method.
"""
messages_grouped = itertools.groupby((
self._extract_user_message(m)
for m in incoming), lambda x: x[0])
requeues = set()
for priority, messages in messages_grouped:
__, raw_messages, messages = six.moves.zip(*messages)
raw_messages = list(raw_messages)
messages = list(messages)
if priority not in PRIORITIES:
LOG.warning(_LW('Unknown priority "%s"'), priority)
continue
for screen, callback in self._callbacks_by_priority.get(priority,
[]):
if screen:
filtered_messages = [message for message in messages
if screen.match(
message["ctxt"],
message["publisher_id"],
message["event_type"],
message["metadata"],
message["payload"])]
else:
filtered_messages = messages
if not filtered_messages:
continue
ret = self._exec_callback(callback, filtered_messages)
if ret == NotificationResult.REQUEUE:
requeues.update(raw_messages)
break
return requeues
def _exec_callback(self, callback, messages):
try:
return callback(messages)
except Exception:
LOG.exception("Callback raised an exception.")
return NotificationResult.REQUEUE
分析:
dispatch(self, incoming)方法
先是抽取出消息体,然后遍历消费者的消费方法和过滤条件,
遍历消息列表,对每各消息通过过滤条件处理后,得到过滤后的消息列表,
然后调用消费者的消费方法来处理过滤后的消息列表,如果处理过程中发生异常,
则会导致消息继续入队。
1.2.3 分析其基类NotificationServerBase
调用:
/usr/lib/python2.7/site-packages/oslo_messaging/notify/listener.py
class NotificationServerBase(msg_server.MessageHandlingServer):
def __init__(self, transport, targets, dispatcher, executor='blocking',
allow_requeue=True, pool=None, batch_size=1,
batch_timeout=None):
super(NotificationServerBase, self).__init__(transport, dispatcher,
executor)
self._allow_requeue = allow_requeue
self._pool = pool
self.targets = targets
self._targets_priorities = set(
itertools.product(self.targets,
self.dispatcher.supported_priorities)
)
self._batch_size = batch_size
self._batch_timeout = batch_timeout
def _create_listener(self):
return self.transport._listen_for_notifications(
self._targets_priorities, self._pool, self._batch_size,
self._batch_timeout
)
1.2.4 分析其基类 MessageHandlingServer
/usr/lib/python2.7/site-packages/oslo_messaging/server.py
@six.add_metaclass(abc.ABCMeta)
class MessageHandlingServer(service.ServiceBase, _OrderedTaskRunner):
"""Server for handling messages.
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.
"""
def __init__(self, transport, dispatcher, executor='blocking'):
"""Construct a message handling server.
The dispatcher parameter is a DispatcherBase instance which is used
for routing request to endpoint for processing.
The executor parameter controls how incoming messages will be received
and dispatched. By default, the most simple executor is used - the
blocking executor. It handles only one message at once. It's
recommended to use threading or eventlet.
:param transport: the messaging transport
:type transport: Transport
:param dispatcher: has a dispatch() method which is invoked for each
incoming request
:type dispatcher: DispatcherBase
:param executor: name of message executor - available values are
'eventlet' and 'threading'
:type executor: str
"""
self.conf = transport.conf
self.conf.register_opts(_pool_opts)
self.transport = transport
self.dispatcher = dispatcher
self.executor_type = executor
if self.executor_type == 'blocking':
debtcollector.deprecate(