消息队列 64式 : 2、oslo.messaging消息处理源码分析

本文深入分析了OpenStack Ceilometer项目的oslo.messaging库中,当executor设置为threading时的消息处理流程。从CollectorService的启动到get_batch_notification_listener方法,再到NotificationDispatcher和线程池ThreadPoolExecutor的使用,详细阐述了消息的接收、分发、处理及确认或重新入队的步骤。最后总结了oslo.messaging中关键类的作用和它们之间的关系。
摘要由CSDN通过智能技术生成

目标:
弄清楚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(
     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值