Celery源码笔记(五)Consumer启动

Consumer启动
Consumer类的启动由Worker类中Blueprint实例调用start函数开始,首先我们来看该函数的定义

class Consumer(object):
    """Consumer blueprint."""

    def start(self):
        blueprint = self.blueprint
        while blueprint.state not in STOP_CONDITIONS:
            maybe_shutdown()
            if self.restart_count:
                try:
                    self._restart_state.step()
                except RestartFreqExceeded as exc:
                    crit('Frequent restarts detected: %r', exc, exc_info=1)
                    sleep(1)
            self.restart_count += 1
            try:
                blueprint.start(self) # 调用blueprint的start函数启动各个组件
            except self.connection_errors as exc:
                # If we're not retrying connections, no need to catch
                # connection errors
                if not self.app.conf.broker_connection_retry:
                    raise
                if isinstance(exc, OSError) and exc.errno == errno.EMFILE:
                    raise  # Too many open files
                maybe_shutdown()
                if blueprint.state not in STOP_CONDITIONS:
                    if self.connection:
                        self.on_connection_error_after_connected(exc)
                    else:
                        self.on_connection_error_before_connected(exc)
                    self.on_close()
                    blueprint.restart(self)

与Worker类似,Consumer的启动过程中,也是通过blueprint调用各个启动步骤的start函数进行启动的。

其他组件启动完毕后,启动event loop组件并开始事件循环,至此Worker启动完成。

组件启动流程

Connection

class Connection(bootsteps.StartStopStep):
    """Service managing the consumer broker connection."""

    def start(self, c):
        c.connection = c.connect() # 调用Consumer的connect函数
        info('Connected to %s', c.connection.as_uri())
class Consumer(object):
    """Consumer blueprint."""
    def connect(self):
        """Establish the broker connection used for consuming tasks.

        Retries establishing the connection if the
        :setting:`broker_connection_retry` setting is enabled
        """
        conn = self.connection_for_read(heartbeat=self.amqheartbeat) # 与队列建立连接
        if self.hub:
            conn.transport.register_with_event_loop(conn.connection, self.hub) # 将连接加入事件循环
        return conn

Connection启动时会调用Consumer的connect函数以连接队列,最终会创建celery.app.amqp.Connection实例,而这里实际上是使用kombu库的Connection与队列连接。连接建立之后,会将Connection注册进kombu库的Transport的事件循环中

Events


class Events(bootsteps.StartStopStep):
    """Service used for sending monitoring events."""

    def start(self, c):
        # flush events sent while connection was down.
        prev = self._close(c)
        dis = c.event_dispatcher = c.app.events.Dispatcher(
            c.connection_for_write(),
            hostname=c.hostname,
            enabled=self.send_events,
            groups=self.groups,
            # we currently only buffer events when the event loop is enabled
            # XXX This excludes eventlet/gevent, which should actually buffer.
            buffer_group=['task'] if c.hub else None,
            on_send_buffered=c.on_send_event_buffered if c.hub else None,
        ) # 创建事件分发器
        if prev:
            dis.extend_buffer(prev)
            dis.flush()

Events主要的功能是创建并初始化了事件分发器,用于分发事件消息,这里创建的是celery.events.dispatcher.EventDispatcher实例

Mingle

class Mingle(bootsteps.StartStopStep):
    """Bootstep syncing state with neighbor workers.

    At startup, or upon consumer restart, this will:

    - Sync logical clocks.
    - Sync revoked tasks.

    """

    def start(self, c):
        self.sync(c)

    def sync(self, c):
        info('mingle: searching for neighbors')
        replies = self.send_hello(c)
        if replies:
            info('mingle: sync with %s nodes',
                 len([reply for reply, value in items(replies) if value]))
            [self.on_node_reply(c, nodename, reply)
             for nodename, reply in items(replies) if reply]
            info('mingle: sync complete')
        else:
            info('mingle: all alone')

Mingle的作用是同步各个Worker的状态,celery的各个Worker使用broker进行通信,详情可以浏览celery.app.control.Control类的定义,以后会进行分析。

Gossip

class Gossip(bootsteps.ConsumerStep):
    """Bootstep consuming events from other workers.

    This keeps the logical clock value up to date.
    """

    def start(self, c):
        super(Gossip, self).start(c)
        self.dispatcher = c.event_dispatcher

Gossip用于处理其他Worker的事件,用于与其他Worker进行通信。

Heart

class Heart(bootsteps.StartStopStep):
    """Bootstep sending event heartbeats.

    This service sends a ``worker-heartbeat`` message every n seconds.
    用于发送心跳信息
    Note:
        Not to be confused with AMQP protocol level heartbeats.
    """

    def start(self, c):
        c.heart = heartbeat.Heart(
            c.timer, c.event_dispatcher, self.heartbeat_interval,
        )
        c.heart.start()

Heart的主要功能是用于发送信条信息,在start函数中创建了celery.worker.heartbeat.Heart类的实例,并调用了该实例的start函数

Tasks

class Tasks(bootsteps.StartStopStep):
    """Bootstep starting the task message consumer."""

    def start(self, c):
        """Start task consumer."""
        c.update_strategies()

        # - RabbitMQ 3.3 completely redefines how basic_qos works..
        # This will detect if the new qos smenatics is in effect,
        # and if so make sure the 'apply_global' flag is set on qos updates.
        qos_global = not c.connection.qos_semantics_matches_spec

        # set initial prefetch count
        c.connection.default_channel.basic_qos(
            0, c.initial_prefetch_count, qos_global,
        )

        c.task_consumer = c.app.amqp.TaskConsumer(
            c.connection, on_decode_error=c.on_decode_error,
        ) # 创建Consumer

        def set_prefetch_count(prefetch_count):
            return c.task_consumer.qos(
                prefetch_count=prefetch_count,
                apply_global=qos_global,
            )
        c.qos = QoS(set_prefetch_count, c.initial_prefetch_count) # 创建QoS

Tasks类用于创建消息的Consumer以及QoS,这里用到的Consumer以及QoS均为kombu库所提供的类。

Control

class Pidbox(object):
    """Worker mailbox."""

    def start(self, c):
        self.node.channel = c.connection.channel() # 获取信道
        self.consumer = self.node.listen(callback=self.on_message) # 监听信道
        self.consumer.on_decode_error = c.on_decode_error

Control类启动的是celery.worker.pidbox.Pidbox类的实例 这里可以看到Pidbox所做的工作即为创建信道之后对信道进行监听,若收到消息后则回调相应的函数进行处理

Event loop

class Evloop(bootsteps.StartStopStep):
    """Event loop service.

    Note:
        This is always started last.
    """

    label = 'event loop'
    last = True

    def start(self, c):
        self.patch_all(c)
        c.loop(*c.loop_args())

    def patch_all(self, c):
        c.qos._mutex = DummyLock()

这里可以看到Evloop的代码极为简单,主要部分是启动事件循环,并且该组件需要在最后启动

你可能感兴趣的:(Celery源码笔记(五)Consumer启动)