Руководство для вкладчиков по кодексу¶
Философия¶
Правило приоритета 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
Фабричные функции и методы должны быть 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.
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
Это настоящий рабочий, построенный вокруг сапог.