Периодические задачи

Введение

celery beat - это планировщик; он запускает задания через регулярные промежутки времени, которые затем выполняются доступными рабочими узлами в кластере.

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

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

Часовые пояса

В расписаниях периодических задач по умолчанию используется часовой пояс UTC, но вы можете изменить используемый часовой пояс с помощью параметра timezone.

Примером часового пояса может быть Европа/Лондон:

timezone = 'Europe/London'

Этот параметр должен быть добавлен в ваше приложение, либо путем его непосредственной настройки с помощью (app.conf.timezone = 'Europe/London'), либо путем добавления его в ваш модуль конфигурации, если вы установили его с помощью app.config_from_object. Более подробную информацию о параметрах конфигурации смотрите в Конфигурация.

Планировщик по умолчанию (хранящий расписание в файле celerybeat-schedule) автоматически определит, что часовой пояс изменился, и сам сбросит расписание, но другие планировщики могут быть не такими умными (например, планировщик базы данных Django, см. ниже), и в этом случае вам придется сбросить расписание вручную.

Пользователи Django

Celery рекомендует и совместим с новой настройкой USE_TZ, представленной в Django 1.4.

Для пользователей Django будет использоваться часовой пояс, указанный в настройке TIME_ZONE, или вы можете указать пользовательский часовой пояс только для Celery с помощью настройки timezone.

Планировщик базы данных не перезагружается при изменении настроек, связанных с часовым поясом, поэтому вы должны сделать это вручную:

$ python manage.py shell
>>> from djcelery.models import PeriodicTask
>>> PeriodicTask.objects.update(last_run_at=None)

Django-Celery поддерживает только Celery 4.0 и ниже, для Celery 4.0 и выше, сделайте следующее:

$ python manage.py shell
>>> from django_celery_beat.models import PeriodicTask
>>> PeriodicTask.objects.update(last_run_at=None)

Записи

Чтобы периодически вызывать задачу, необходимо добавить запись в список расписания ударов.

from celery import Celery
from celery.schedules import crontab

app = Celery()

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')

    # Calls test('world') every 30 seconds
    sender.add_periodic_task(30.0, test.s('world'), expires=10)

    # Executes every Monday morning at 7:30 a.m.
    sender.add_periodic_task(
        crontab(hour=7, minute=30, day_of_week=1),
        test.s('Happy Mondays!'),
    )

@app.task
def test(arg):
    print(arg)

@app.task
def add(x, y):
    z = x + y
    print(z)

Установка их из обработчика on_after_configure означает, что при использовании test.s() мы не будем оценивать приложение на уровне модуля. Обратите внимание, что on_after_configure отправляется после установки приложения, поэтому задачи вне модуля, в котором объявлено приложение (например, в файле tasks.py, расположенном по celery.Celery.autodiscover_tasks()), должны использовать более поздний сигнал, например on_after_finalize.

Функция add_periodic_task() добавит запись в настройку beat_schedule за кадром, и эту же настройку можно использовать для установки периодических заданий вручную:

Пример: Запускайте задачу tasks.add каждые 30 секунд.

app.conf.beat_schedule = {
    'add-every-30-seconds': {
        'task': 'tasks.add',
        'schedule': 30.0,
        'args': (16, 16)
    },
}
app.conf.timezone = 'UTC'

Примечание

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

Если вы хотите использовать кортеж из одного элемента для args, не забудьте, что конструктор - это запятая, а не пара круглых скобок.

Использование timedelta для расписания означает, что задание будет отправляться с интервалом в 30 секунд (первое задание будет отправлено через 30 секунд после запуска celery beat, а затем каждые 30 секунд после последнего запуска).

Существует также расписание, подобное Crontab, см. раздел Crontab schedules.

Как и в случае с cron, задачи могут накладываться друг на друга, если первая задача не завершится раньше следующей. Если это вызывает беспокойство, следует использовать стратегию блокировки, чтобы гарантировать, что одновременно может выполняться только один экземпляр (см., например, Обеспечение выполнения задания только по одному разу).

Доступные поля

  • задача

    Имя задания для выполнения.

  • schedule

    Частота выполнения.

    Это может быть число секунд как целое число, timedelta, или crontab. Вы также можете определить свои собственные типы расписаний, расширив интерфейс schedule.

  • args

    Позиционные аргументы (list или tuple).

  • kwargs

    Аргументы ключевых слов (dict).

  • опции

    Варианты исполнения (dict).

    Это может быть любой аргумент, поддерживаемый apply_async()exchange, routing_key, expires и так далее.

  • относительный

    Если relative равно true timedelta расписания планируются «по часам». Это означает, что частота округляется до ближайшей секунды, минуты, часа или дня в зависимости от периода timedelta.

    По умолчанию relative равно false, частота не округляется и будет относиться к моменту запуска celery beat.

Расписания Crontab

Если вы хотите больше контролировать время выполнения задания, например, определенное время суток или день недели, вы можете использовать тип расписания crontab:

from celery.schedules import crontab

app.conf.beat_schedule = {
    # Executes every Monday morning at 7:30 a.m.
    'add-every-monday-morning': {
        'task': 'tasks.add',
        'schedule': crontab(hour=7, minute=30, day_of_week=1),
        'args': (16, 16),
    },
}

Синтаксис этих выражений Crontab очень гибкий.

Некоторые примеры:

Пример.

Смысл

crontab()

Выполняйте каждую минуту.

crontab(minute=0, hour=0)

Выполнять ежедневно в полночь.

crontab(minute=0, hour='*/3')

Выполнять каждые три часа: полночь, 3 утра, 6 утра, 9 утра, полдень, 3 вечера, 6 вечера, 9 вечера.

crontab(minute=0,

hour='0,3,6,9,12,15,18,21')

То же, что и предыдущий.

crontab(minute='*/15')

Выполняется каждые 15 минут.

crontab(day_of_week='sunday')

Выполняется каждую минуту (!) по воскресеньям.

crontab(minute='*',

hour='*', day_of_week='sun')

То же, что и предыдущий.

crontab(minute='*/10',

hour='3,17,22', day_of_week='thu,fri')

Выполнять каждые десять минут, но только между 3-4 часами утра, 5-6 часами вечера и 10-11 часами вечера по четвергам или пятницам.

crontab(minute=0, hour='*/2,*/3')

Выполнять каждый четный час и каждый час, кратный трем. Это означает: в каждый час за исключением: 1 утра, 5 утра, 7 утра, 11 утра, 1 вечера, 5 вечера, 7 вечера, 11 вечера.

crontab(minute=0, hour='*/5')

Выполнение часа, кратного 5. Это означает, что он срабатывает в 15:00, а не в 17:00 (так как 15:00 равно 24-часовому значению часов «15», которое делится на 5).

crontab(minute=0, hour='*/3,8-17')

Выполнять каждый час, кратный 3, и каждый час в рабочее время (с 8 утра до 5 вечера).

crontab(0, 0, day_of_month='2')

Выполнять во второй день каждого месяца.

crontab(0, 0,

day_of_month='2-30/2')

Выполняйте в каждый четный день.

crontab(0, 0,

day_of_month='1-7,15-21')

Выполнять в первую и третью недели месяца.

crontab(0, 0, day_of_month='11',

month_of_year='5')

Выполняется одиннадцатого мая каждого года.

crontab(0, 0,

month_of_year='*/3')

Выполнять каждый день в первый месяц каждого квартала.

Дополнительную документацию см. в разделе celery.schedules.crontab.

Солнечные графики

Если у вас есть задание, которое должно выполняться в соответствии с восходом, закатом, рассветом или сумерками, вы можете использовать тип расписания solar:

from celery.schedules import solar

app.conf.beat_schedule = {
    # Executes at sunset in Melbourne
    'add-at-melbourne-sunset': {
        'task': 'tasks.add',
        'schedule': solar('sunset', -37.81753, 144.96715),
        'args': (16, 16),
    },
}

Аргументы просты: solar(event, latitude, longitude)

Обязательно используйте правильный знак для обозначения широты и долготы:

Подписаться

Аргумент

Смысл

+

latitude

Север

-

latitude

Юг

+

longitude

Восток

-

longitude

Запад

Возможными типами событий являются:

Событие

Смысл

dawn_astronomical

Выполните в момент, после которого небо уже не будет полностью темным. Это происходит, когда солнце находится на 18 градусов ниже горизонта.

dawn_nautical

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

dawn_civil

Выполняйте, когда света достаточно для того, чтобы объекты были различимы, и можно было приступать к активному отдыху; формально, когда Солнце находится на 6 градусов ниже горизонта.

sunrise

Выполняется, когда верхний край солнца появляется над восточным горизонтом утром.

solar_noon

Выполняется, когда солнце находится выше всего над горизонтом в этот день.

sunset

Выполняется, когда край солнца исчезает над западным горизонтом в вечернее время.

dusk_civil

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

dusk_nautical

Выполняется, когда солнце находится на 12 градусов ниже горизонта. Объекты больше не различимы, а горизонт больше не виден невооруженным глазом.

dusk_astronomical

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

Все солнечные события рассчитываются по UTC, поэтому на них не влияют настройки вашего часового пояса.

В полярных регионах солнце может всходить и заходить не каждый день. Планировщик способен обрабатывать такие случаи (т.е. событие sunrise не будет выполняться в день, когда солнце не восходит). Единственным исключением является solar_noon, которое формально определяется как момент прохождения солнца через небесный меридиан, и будет происходить каждый день, даже если солнце находится ниже горизонта.

Сумерки определяются как период между рассветом и восходом солнца, а также между закатом и сумерками. Вы можете запланировать событие в соответствии с «сумерками» в зависимости от вашего определения сумерек (гражданские, морские или астрономические), а также от того, хотите ли вы, чтобы событие произошло в начале или конце сумерек, используя соответствующее событие из списка выше.

Дополнительную документацию см. в разделе celery.schedules.solar.

Запуск планировщика

Чтобы запустить службу celery beat:

$ celery -A proj beat

Вы также можете встроить beat внутрь рабочего, включив опцию workers -B, это удобно, если вы никогда не будете запускать более одного рабочего узла, но это не часто используется и по этой причине не рекомендуется для производственного использования:

$ celery -A proj worker -B

Beat должен хранить последнее время выполнения задач в локальном файле базы данных (по умолчанию он называется celerybeat-schedule), поэтому ему нужен доступ для записи в текущий каталог, или же вы можете указать собственное местоположение для этого файла:

$ celery -A proj beat -s /home/celery/var/run/celerybeat-schedule

Примечание

Чтобы демонизировать beat, смотрите Демонизация.

Использование пользовательских классов планировщика

Пользовательские классы планировщиков могут быть заданы в командной строке (аргумент --scheduler).

По умолчанию используется планировщик celery.beat.PersistentScheduler, который просто отслеживает время последних запусков в локальном файле базы данных shelve.

Существует также расширение django-celery-beat, которое хранит расписание в базе данных Django и представляет удобный интерфейс администратора для управления периодическими задачами во время выполнения.

Чтобы установить и использовать это расширение:

  1. Используйте pip для установки пакета:

    $ pip install django-celery-beat
    
  2. Добавьте модуль django_celery_beat в INSTALLED_APPS в ваш проект Django“ settings.py:

    INSTALLED_APPS = (
        ...,
        'django_celery_beat',
    )
    

    Обратите внимание, что в имени модуля нет тире, только подчеркивание.

  3. Примените миграции базы данных Django, чтобы были созданы необходимые таблицы:

    $ python manage.py migrate
    
  4. Запустите службу celery beat с помощью планировщика django_celery_beat.schedulers:DatabaseScheduler:

    $ celery -A proj beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler
    

    Примечание: Вы также можете добавить это в качестве параметра beat_scheduler напрямую.

  5. Посетите интерфейс Django-Admin, чтобы установить некоторые периодические задачи.

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