Руководство для вкладчиков по кодексу

Философия

Правило приоритета API>RCP

  • API важнее читабельности

  • Читабельность важнее, чем конвенция

  • Конвенция важнее производительности
    • …если только код не является проверенной горячей точкой.

Более важным, чем что-либо другое, является API конечного пользователя. Традиции должны отойти в сторону, и любые страдания всегда облегчаются, если конечным результатом является лучший API.

Используемые понятия и идиомы

Занятия

Наименование

  • Следующие PEP 8.

  • Имена классов должны быть в CamelCase.

  • но не если это глаголы, глаголы должны быть в нижнем регистре:

    # - test case for a class
    class TestMyClass(Case):                # BAD
        pass
    
    class test_MyClass(Case):               # GOOD
        pass
    
    # - test case for a function
    class TestMyFunction(Case):             # BAD
        pass
    
    class test_my_function(Case):           # GOOD
        pass
    
    # - "action" class (verb)
    class UpdateTwitterStatus:    # BAD
        pass
    
    class update_twitter_status:    # GOOD
        pass
    

    Примечание

    Иногда имеет смысл иметь маску класса в виде функции, и этому есть прецедент в стандартной библиотеке Python (например, contextmanager). Примеры Celery включают signature, chord, inspect, promise и многое другое.

  • Фабричные функции и методы должны быть CamelCase (за исключением глаголов):

    class Celery:
    
        def consumer_factory(self):     # BAD
            ...
    
        def Consumer(self):             # GOOD
            ...
    

Значения по умолчанию

Атрибуты класса служат значениями по умолчанию для экземпляра, поскольку это означает, что они могут быть установлены как при инстанцировании, так и при наследовании.

Пример:

class Producer:
    active = True
    serializer = 'json'

    def __init__(self, serializer=None, active=None):
        self.serializer = serializer or self.serializer

        # must check for None when value can be false-y
        self.active = active if active is not None else self.active

Подкласс может изменить значение по умолчанию:

TaskProducer(Producer):
    serializer = 'pickle'

и значение может быть установлено при инстанцировании:

>>> producer = TaskProducer(serializer='msgpack')

Исключения

Пользовательские исключения, вызываемые методами и свойствами объектов, должны быть доступны как атрибут и документированы в методе/свойстве, которое вызывает исключение.

Таким образом, пользователю не нужно выяснять, откуда импортировать исключение, а достаточно использовать help(obj) и получить доступ к классу исключения непосредственно из экземпляра.

Пример:

class Empty(Exception):
    pass

class Queue:
    Empty = Empty

    def get(self):
        """Get the next item from the queue.

        :raises Queue.Empty: if there are no more items left.

        """
        try:
            return self.queue.popleft()
        except IndexError:
            raise self.Empty()

Композиты

Аналогично исключениям, составные классы должны быть доступны для переопределения при наследовании и/или инстанцировании. При выборе классов можно руководствоваться здравым смыслом, но часто лучше добавить один слишком много: предсказать, что пользователям нужно переопределить, очень сложно (это спасло нас от многих «обезьяньих» исправлений).

Пример:

class Worker:
    Consumer = Consumer

    def __init__(self, connection, consumer_cls=None):
        self.Consumer = consumer_cls or self.Consumer

    def do_work(self):
        with self.Consumer(self.connection) as consumer:
            self.connection.drain_events()

Приложения против «одиночного режима»

Вначале Celery был разработан для Django, просто потому, что это позволило нам быстро запустить проект и при этом иметь большую потенциальную базу пользователей.

В Django существует глобальный объект настроек, поэтому несколько проектов Django не могут сосуществовать в одном пространстве процессов, что впоследствии создало проблему для использования Celery с фреймворками, не имеющими такого ограничения.

Поэтому была введена концепция приложений. При использовании приложений вы используете объекты „celery“ вместо импорта вещей из подмодулей Celery, это (к сожалению) также означает, что у Celery по существу есть два API.

Вот пример использования Celery в одномодовом режиме:

from celery import task
from celery.task.control import inspect

from .models import CeleryStats

@task
def write_stats_to_db():
    stats = inspect().stats(timeout=1)
    for node_name, reply in stats:
        CeleryStats.objects.update_stat(node_name, stats)

и вот то же самое с использованием объектов приложения Celery:

from .celery import celery
from .models import CeleryStats

@app.task
def write_stats_to_db():
    stats = celery.control.inspect().stats(timeout=1)
    for node_name, reply in stats:
        CeleryStats.objects.update_stat(node_name, stats)

В приведенном выше примере реальный экземпляр приложения импортируется из модуля в проекте, этот модуль может выглядеть примерно так:

from celery import Celery

app = Celery(broker='amqp://')

Обзор модулей

  • celery.app

    Это ядро Celery: точка входа для всей функциональности.

  • celery.loaders

    Каждое приложение должно иметь загрузчик. Загрузчик решает, как считывается конфигурация; что происходит при запуске рабочего; когда начинается и заканчивается задача и так далее.

    В комплект входят следующие погрузчики:

    • приложение

      Пользовательские экземпляры приложений Celery используют этот загрузчик по умолчанию.

    • по умолчанию

      «single-mode» использует этот загрузчик по умолчанию.

    Существуют также загрузчики расширений, например celery-pylons.

  • сельдерей.рабочий

    Это рабочая реализация.

  • celery.backends

    Бэкенды результатов задач находятся здесь.

  • celery.apps

    Основные пользовательские приложения: worker и beat. Обертки командной строки для них находятся в файле celery.bin (см. ниже)

  • сельдерей.bin

    Приложения командной строки. setup.py создает точки входа setuptools для них.

  • celery.concurrency

    Реализации пула исполнения (prefork, eventlet, gevent, solo, thread).

  • celery.db

    Модели базы данных для бэкенда результатов базы данных SQLAlchemy. (следует перенести в celery.backends.database)

  • сельдерей.события

    Отправка и потребление событий мониторинга, также включает монитор curses, дампер событий и утилиты для работы с состоянием кластера in-memory.

  • celery.execute.trace

    Как задания выполняются и отслеживаются рабочим, а также в режиме eager.

  • сельдерей.безопасность

    Функциональность, связанная с безопасностью, в настоящее время это сериализатор, использующий криптографические дайджесты.

  • сельдерей.задание

    однорежимный интерфейс для создания задач и управления работниками.

  • t.unit (int distribution)

    Набор модульных тестов.

  • celery.utils

    Утилитарные функции, используемые в кодовой базе Celery. Большая часть из них совместима с разными версиями Python.

  • celery.contrib

    Дополнительный публичный код, который не помещается ни в какое другое пространство имен.

Обзор работника

  • celery.bin.worker:Worker.

    Это интерфейс командной строки для рабочего.

    Обязанности:
    • Демонизация при установке --detach,

    • сброс привилегий при использовании аргументов --uid/ --gid

    • Устанавливает «патчи параллелизма» (патчи eventlet/gevent monkey).

    app.worker_main(argv) вызывает instantiate('celery.bin.worker:Worker')(app).execute_from_commandline(argv)

  • app.Worker -> celery.apps.worker:Worker

    Обязанности: * устанавливает логирование и перенаправляет стандартные ауты * устанавливает обработчики сигналов (TERM/HUP/STOP/USR1 (cry)/USR2 (rdb)) * печатает баннеры и предупреждения (например, pickle warning) * обрабатывает аргумент celery worker --purge.

  • app.WorkController -> celery.worker.WorkController

    Это настоящий рабочий, построенный вокруг сапог.

Вернуться на верх