Celery: функция add_periodic_task не запускает задачу

Созданный на 13 нояб. 2016  ·  33Комментарии  ·  Источник: celery/celery

Контрольный список

  • [x] Я включил в этот выпуск вывод celery -A proj report .
    (если у вас нет такой возможности, то хотя бы укажите Celery
    версия затронута).
  • [x] Я подтвердил, что проблема существует с ветвью master Celery.

Действия по воспроизведению

tasks.py

from celery import Celery

celery = Celery('tasks', broker='pyamqp://guest@localhost//')

@celery.task
def add(x, y):
    return x + y


@celery.on_after_configure.connect
def add_periodic():
    celery.add_periodic_task(10.0, add.s(2,3), name='add every 10')

if __name__ == '__main__':
        add_periodic()


шаг 1: rabbitmq запущен
rabbitmq 1186 1 0 12 ноября? 00:00:00 / bin / sh / usr / sbin / rabbitmq-сервер

шаг 2: выполнить tasks.py
python tasks.py
step3: начать бить воркера
celery -A tasks -l info beat
celery beat v4.0.0 (скрытый вызов) запускается.
__ - ... __ - _
LocalTime -> 2016-11-12 17:37:58
Конфигурация ->
. брокер -> amqp: // гость: ** @localhost : 5672 //
. загрузчик -> celery.loaders.app.AppLoader
. планировщик -> celery.beat.PersistentScheduler
. db -> celerybeat-расписание
. файл журнала -> [stderr] @% INFO
. maxinterval -> 5.00 минут (300 с)
[2016-11-12 17: 37: 58,912: INFO / MainProcess] beat: Запуск ...

Ожидаемое поведение

Я ожидаю, что планировщик будет запускать функцию add () каждые десять секунд.

Фактическое поведение

Функция add () не запускается.
Никаких исключений в терминале не вижу. Я что-нибудь упускаю?

Celerybeat Bug Report

Самый полезный комментарий

Чтобы запускать периодические задачи, вы должны также вызвать планировщик при запуске воркера с помощью опции -B :

celery -A proj worker -B

При использовании сельдерея в приложениях django, где задачи автоматически обнаруживаются из приложений, вам нужно использовать сигнал on_after_finalize вместо on_after_configure .

Рекомендации:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

Все 33 Комментарий

У меня такая же проблема :(

Ваш пример мне подходит.

ПРИМЕЧАНИЕ. Ваш обработчик сигнала должен принять ** kwargs, в противном случае это будет ошибкой в ​​будущем.

Используя ваш пример

# file: tasks.py
from celery import Celery

celery = Celery('tasks', broker='pyamqp://guest@localhost//')

@celery.task
def add(x, y):
    return x + y


@celery.on_after_configure.connect
def add_periodic(**kwargs):
    celery.add_periodic_task(10.0, add.s(2,3), name='add every 10')

Запускаю бит-сервис следующим образом:

$ celery -A tasks beat -l debug
celery beat v4.0.0 (latentcall) is starting.
__    -    ... __   -        _
LocalTime -> 2016-12-01 11:54:56
Configuration ->
    . broker -> amqp://guest:**<strong i="11">@localhost</strong>:5672//
    . loader -> celery.loaders.app.AppLoader
    . scheduler -> celery.beat.PersistentScheduler
    . db -> celerybeat-schedule
    . logfile -> [stderr]@%DEBUG
    . maxinterval -> 5.00 minutes (300s)
[2016-12-01 11:54:56,511: DEBUG/MainProcess] Setting default socket timeout to 30
[2016-12-01 11:54:56,511: INFO/MainProcess] beat: Starting...
[2016-12-01 11:54:56,517: DEBUG/MainProcess] Current schedule:
<ScheduleEntry: celery.backend_cleanup celery.backend_cleanup() <crontab: 0 4 * * * (m/h/d/dM/MY)>
<ScheduleEntry: add every 10 tasks.add(2, 3) <freq: 10.00 seconds>
[2016-12-01 11:54:56,517: DEBUG/MainProcess] beat: Ticking with max interval->5.00 minutes
[2016-12-01 11:54:56,528: DEBUG/MainProcess] Start from server, version: 0.9, properties: {'information': 'Licensed under the MPL.  See http://www.rabbitmq.com/', 'product': 'RabbitMQ', 'copyright': 'Copyright (C) 2007-2016 Pivotal Software, Inc.', 'capabilities': {'exchange_exchange_bindings': True, 'connection.blocked': True, 'authentication_failure_close': True, 'direct_reply_to': True, 'basic.nack': True, 'per_consumer_qos': True, 'consumer_priorities': True, 'consumer_cancel_notify': True, 'publisher_confirms': True}, 'cluster_name': 'rabbit<strong i="12">@grain</strong>', 'platform': 'Erlang/OTP', 'version': '3.6.4'}, mechanisms: [u'AMQPLAIN', u'PLAIN'], locales: [u'en_US']
[2016-12-01 11:54:56,531: INFO/MainProcess] Scheduler: Sending due task add every 10 (tasks.add)
[2016-12-01 11:54:56,534: DEBUG/MainProcess] using channel_id: 1
[2016-12-01 11:54:56,535: DEBUG/MainProcess] Channel open
[2016-12-01 11:54:56,537: DEBUG/MainProcess] beat: Synchronizing schedule...
[2016-12-01 11:54:56,537: DEBUG/MainProcess] tasks.add sent. id->af224838-cf72-4d0d-9076-1c39cdbeffb8
[2016-12-01 11:54:56,537: DEBUG/MainProcess] beat: Waking up in 9.97 seconds.
[2016-12-01 11:55:06,519: INFO/MainProcess] Scheduler: Sending due task add every 10 (tasks.add)
[2016-12-01 11:55:06,520: DEBUG/MainProcess] tasks.add sent. id->907cf307-e36f-455a-97a8-441c79b8ab92

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

Это моя ветка:

from __future__ import absolute_import, unicode_literals
import threading
from celery import current_app
from celery.bin import worker

app = current_app._get_current_object()


class CeleryThread(threading.Thread):
    def __init__(self):
        super(CeleryThread, self).__init__()

        self.app = app
        self.worker = worker.worker(app=self.app)

        self.options = {
            'broker': 'amqp://guest:guest<strong i="7">@localhost</strong>:5672//',
            'loglevel': 'INFO',
            'traceback': True,
        }

        app.add_periodic_task(5.0, test.s('hello'), name='add every 10')

    def run(self):
        self.worker.run(**self.options)


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

И main.py для запуска этого

celery_thread = CeleryThread()
# used to kill the thread when the main program stop
# celery_thread.daemon = True
celery_thread.start()

Мой вывод на консоль

 -------------- celery<strong i="14">@ubuntu</strong> v4.0.0 (latentcall)
---- **** ----- 
--- * ***  * -- Linux-4.4.0-51-generic-x86_64-with-Ubuntu-16.04-xenial 2016-12-03 14:33:10
-- * - **** --- 
- ** ---------- [config]
- ** ---------- .> app:         default:0x7f75775bfc50 (.default.Loader)
- ** ---------- .> transport:   amqp://guest:**<strong i="15">@localhost</strong>:5672//
- ** ---------- .> results:     disabled://
- *** --- * --- .> concurrency: 4 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** ----- 
 -------------- [queues]
                .> celery           exchange=celery(direct) key=celery


[tasks]
  . kalliope.core.CrontabManager2.CeleryThread.test

[2016-12-03 14:33:10,458: INFO/MainProcess] Connected to amqp://guest:**@127.0.0.1:5672//
[2016-12-03 14:33:10,466: INFO/MainProcess] mingle: searching for neighbors
[2016-12-03 14:33:11,486: INFO/MainProcess] mingle: all alone
[2016-12-03 14:33:11,515: INFO/MainProcess] celery<strong i="16">@ubuntu</strong> ready.

Я забыл вариант? Я вижу, что в выходных данных @ask установлен "планировщик".

Заранее благодарим за любую помощь.

Та же конфигурация с @ liuallen1981 и та же проблема. Кто-нибудь выясняет, что происходит? На данный момент я должен использовать

celery.conf.beat_schedule = {
    'do-something-periodically': {
        'task': 'tasks.my_task',
        'schedule': 3.0,
    },
}

вместо использования функции setup_periodic_tasks с декоратором on_after_configure.connect .

+1 Также есть эта проблема.

+1 Также есть эта проблема.
Версия сельдерея 4.0.2 (скрытый вызов)

+1 Также есть эта проблема.

+1 Также есть эта проблема. Продолжил и протестировал с

Сельдерей: 4.0.2

Чтобы запускать периодические задачи, вы должны также вызвать планировщик при запуске воркера с помощью опции -B :

celery -A proj worker -B

При использовании сельдерея в приложениях django, где задачи автоматически обнаруживаются из приложений, вам нужно использовать сигнал on_after_finalize вместо on_after_configure .

Рекомендации:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

-B не предназначен для производства и просто запускает планировщик Beats, который, по крайней мере, в моем случае уже запущен.

+1 такая же проблема с сельдереем (4.0.2)

Такая же проблема здесь ....

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

+1

такая же проблема здесь

такая же проблема здесь,

и я пытаюсь напечатать что-то внутри обратного вызова, кажется, обратный вызов не был вызван, но RabbitMQ работает (отлично работает, когда я запускаю задачу в коде)

@celery.on_after_configure.connect
def setup_periodic_tasks(**kwargs):
    print('after connect')
(py35) ➜  celery -A celery.beat beat
celery beat v4.0.2 (latentcall) is starting.
__    -    ... __   -        _
LocalTime -> 2017-08-08 02:42:18
Configuration ->
    . broker -> amqp://**:**@**:5672//
    . loader -> celery.loaders.app.AppLoader
    . scheduler -> celery.beat.PersistentScheduler
    . db -> celerybeat-schedule
    . logfile -> [stderr]@%WARNING
    . maxinterval -> 5.00 minutes (300s)

Я использую Celery config celery.conf.beat_schedule вместо динамического add_periodic_task чтобы решить эту проблему, поскольку мне не нужно устанавливать расписание динамически, но я все еще не знаю, почему возникает эта проблема.

Я прошел через библиотеку и обнаружил, что мой слушатель сигналов был создан / прикреплен после того, как был запущен сигнал on_after_configure . (Я помещал свой слушатель сигнала в app/tasks.py и он не работал.)

Я рассудил, что сигнал готовности приложения Django, вероятно, будет выполнен после настройки Celery, и до сих пор он у меня работает хорошо.

ПРИМЕЧАНИЕ: я не уверен, что на самом деле влечет за собой конфигурация сельдерея и возможно ли, что app.ready может сработать до того, как будет настроен Celery ... однако я ожидаю, что это, по крайней мере, вызовет ошибку времени выполнения.

Пример кода из моего app/apps.py :

from django.apps import AppConfig
import django.db.utils
from celery_app import app as celery_app
from celery.schedules import crontab
import utils.cron


class MyAppConfig(AppConfig):
    name = 'app'
    verbose_name = "MyApp"

    def ready(self):
        print("MyAppConfig.ready invoked.")
        import app.signals

        print("* * * Setting up periodic tasks!")
        import app.models
        import app.tasks
        for cron in app.models.CronTask.objects.all():
            celery_app.add_periodic_task(
                crontab(**utils.cron.parse_crontab_expression_to_celery(cron.frequency)),
                app.tasks.do_cron.s(cron.id),
                name='do cron'
            )

Обратите внимание, что вам также необходимо указать INSTALLED_APPS на использование вашего нового AppConfig в settings.py :

INSTALLED_APPS = [
    # ...
    'app.apps.MyAppConfig',
]

Хорошим подходом или исправлением , вероятно, было бы написать новый декоратор, который 1) проверяет, настроен ли уже Celery, и если да, то выполняется немедленно и 2) если Celery не настроен, добавляет слушателя, используя @celery.on_after_configure.connect .

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

CCing @rustanacexd @viennadd, чтобы вы могли попробовать это исправление, если вам все еще нужно динамически планировать задачи?

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

Пример:

@celery.on_after_configure.connect
def setup_periodic_tasks(**kwargs):
    celery.add_periodic_task(5.0, do_thing_b.s(), name='Test B')
    celery.add_periodic_task(4.9, do_thing_a.s(), name='Test A')

Такой заказ означает, что do_thing_a никогда не сработает, так как он будет перезаписан на do_thing_b . Первоначально для них обоих было установлено значение 5 , хотя сработал только один (я считаю, что в этом случае это был бы B, поскольку он был добавлен первым). Затем я изменил его на десятичное число и заменил его на .1 чтобы посмотреть, исправит ли это это. Никаких кубиков. Затем я приказал им, чтобы нижний срабатывал первым, а верхний срабатывал вторым, и на этом все было исправлено. Т.е.:

@celery.on_after_configure.connect
def setup_periodic_tasks(**kwargs):
    celery.add_periodic_task(4.9, do_thing_b.s(), name='Test B')
    celery.add_periodic_task(5.0, do_thing_a.s(), name='Test A')

Мы также используем некоторые crontab() s, хотя это своего рода загадка для запуска, поскольку некоторые работают, а некоторые нет, я подозреваю, что это та же проблема, что и выше. Я не играл с ним полностью, так как эти интервалы обычно устанавливаются каждые X часов / дней, поэтому я обычно забываю, что они существуют.

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

Мне удалось это сделать. Проверьте мой ответ здесь:

https://stackoverflow.com/a/46965132/560945

@ prasanna-balaraman Кажется, это работает, спасибо за предложение!

У меня такая же проблема: я протестирую другое решение: qaru.site/questions/411 / ...

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

Мне потребовалось время, чтобы понять, что если в setup_periodic_tasks возникает какое-либо исключение, оно будет подавлено без уведомления.

Функция вызывается здесь: https://github.com/celery/celery/blob/master/celery/app/base.py#L950

Если что-то пойдет не так, исключение сохраняется только в ответах, без повторного запуска или регистрации:
https://github.com/celery/celery/blob/master/celery/utils/dispatch/signal.py#L276

Поэтому я предлагаю максимально упростить setup_periodic_tasks.
Надеюсь это поможет!

@ chiang831 есть ли у вас предложения по его улучшению? Если это так, пожалуйста, отправьте PR или откройте обсуждение в списке рассылки сельдерея

Определение их в on_after_finalize - это то, что сработало для меня (приложение для сельдерея, отличное от django).

@app.on_after_finalize.connect
def app_ready(**kwargs):
    """
    Called once after app has been finalized.
    """
    sender = kwargs.get('sender')

    # periodic tasks
    speed = 15
    sender.add_periodic_task(speed, update_leases.s(),
        name='update leases every {} seconds'.format(speed))

Просто столкнулся с этим, и ни одно из предыдущих решений не помогло мне. Точные сценарии, вызывающие это, сбивают с толку и зависят от поведения подсчета ссылок / gc и точного времени жизни ваших украшенных функций.

Signal.connect по умолчанию содержит только слабую ссылку на обработчик сигнала. Это имеет смысл для других случаев использования объекта Signal (короткоживущий объект, который связывает сигналы, не должен поддерживаться его обработчиками сигналов), но в этом случае очень удивительно.

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

def call_every_5_min(task):
    @app.on_after_configure.connect
    def register_task(sender, **_):
        sender.add_periodic_task(collect_every_m*60, task.signature())

<strong i="8">@call_every_5_min</strong>
<strong i="9">@task</strong>
def my_celery_task(_):
     pass

Исправление состоит в том, чтобы явно запросить сильную ссылку:

def call_every_5_min(task):
    def register_task(sender, **_):
        sender.add_periodic_task(collect_every_m*60, task.signature())
    app.on_after_configure.connect(register_task, weak=False)

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

Я рекомендую изменить документы, чтобы передать weak=False , что должно работать в перечисленных выше случаях. Однако я не тестировал это явно в контексте Django.

Чтобы запускать периодические задачи, вы должны также вызвать планировщик при запуске воркера с помощью опции -B :

celery -A proj worker -B

При использовании сельдерея в приложениях django, где задачи автоматически обнаруживаются из приложений, вам нужно использовать сигнал on_after_finalize вместо on_after_configure .

Рекомендации:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

Мой процесс python -m celery -A app_name worker -l info --autoscale=20,5 -BE заблокирован в конце app_name.celery.py при использовании on_after_finalize .

Та же конфигурация с @ liuallen1981 и та же проблема. Кто-нибудь выясняет, что происходит? На данный момент я должен использовать

celery.conf.beat_schedule = {
    'do-something-periodically': {
        'task': 'tasks.my_task',
        'schedule': 3.0,
    },
}

вместо использования функции setup_periodic_tasks с декоратором on_after_configure.connect .

Вместо этого это работает для меня.

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

мы должны закрыть эту проблему как не ошибку?

@auvipy не уверен. Похоже, это жук сельдерея

Это ошибка, которую мы должны исправить.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги