Celery: La fonction add_periodic_task ne déclenche pas la tâche

Créé le 13 nov. 2016  ·  33Commentaires  ·  Source: celery/celery

Liste de contrôle

  • [x] J'ai inclus la sortie de celery -A proj report dans le numéro.
    (si vous n'êtes pas en mesure de le faire, spécifiez au moins le céleri
    version affectée).
  • [x] J'ai vérifié que le problème existe avec la branche master de Celery.

Étapes à suivre pour reproduire

tâches.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()


étape 1: rabbitmq est en place
rabbitmq 1186 1 0 nov12? 00:00:00 / bin / sh / usr / sbin / rabbitmq-server

étape2: exécutez tasks.py
python tasks.py
Étape 3: commencez à battre le travailleur
celery -A tasks -l info beat
le battement de céleri v4.0.0 (latentcall) démarre.
__ - ... __ - _
Heure locale -> 2016-11-12 17:37:58
Configuration ->
. courtier -> amqp: // invité: ** @localhost : 5672 //
. chargeur -> celery.loaders.app.AppLoader
. planificateur -> celery.beat.PersistentScheduler
. db -> céleri-calendrier
. fichier journal -> [stderr] @% INFO
. maxinterval -> 5,00 minutes (300s)
[2016-11-12 17: 37: 58,912: INFO / MainProcess] beat: Démarrage ...

Comportement prévisible

Je m'attends à ce que le planificateur déclenche la fonction add () toutes les dix secondes.

Comportement réel

La fonction add () ne se déclenche pas.
Je ne vois aucune exception dans le terminal. Est-ce que je rate quelque chose?

Celerybeat Bug Report

Commentaire le plus utile

Pour exécuter des tâches périodiques, vous devez également appeler le planificateur lors du démarrage d'un worker en utilisant l'option -B :

celery -A proj worker -B

Lorsque vous utilisez du céleri dans les applications django, où les tâches sont détectées automatiquement à partir des applications, vous devez utiliser le signal on_after_finalize au lieu de on_after_configure .

Les références:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

Tous les 33 commentaires

J'ai eu le même problème :(

Votre exemple fonctionne bien pour moi.

REMARQUE: votre gestionnaire de signaux doit accepter ** kwargs, faute de quoi cela entraînera une erreur dans le futur.

En utilisant votre exemple

# 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')

Je lance le service beat comme suit:

$ 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

Salut, j'ai le même problème. Mais j'essaye de démarrer le céleri par programmation dans un fil. c'est peut-être la cause.

Ceci est mon fil:

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

Et le main.py pour lancer ceci

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

La sortie de ma console est

 -------------- 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.

Dois-je oublier une option? Je peux voir que vous avez un "planificateur" défini dans votre sortie @ask

Merci d'avance pour toute aide.

La même configuration avec @ liuallen1981 et le même problème. Quelqu'un comprend ce qui se passe?. Pour l'instant je dois utiliser

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

au lieu d'utiliser une fonction setup_periodic_tasks avec le décorateur on_after_configure.connect .

+1 J'ai également ce problème.

+1 J'ai également ce problème.
Céleri version 4.0.2 (appel latent)

+1 J'ai également ce problème.

+1 J'ai également ce problème. Je suis allé et testé avec le code de @ liuallen1981 et

Céleri: 4.0.2

Pour exécuter des tâches périodiques, vous devez également appeler le planificateur lors du démarrage d'un worker en utilisant l'option -B :

celery -A proj worker -B

Lorsque vous utilisez du céleri dans les applications django, où les tâches sont détectées automatiquement à partir des applications, vous devez utiliser le signal on_after_finalize au lieu de on_after_configure .

Les références:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

-B n'est pas destiné à la production et démarre simplement le planificateur Beats qui, au moins dans mon cas, est déjà en cours d'exécution.

+1 ayant le même problème avec le céleri (4.0.2)

Même problème ici ...

vous venez de démarrer un service de battement, devrait également démarrer un travailleur pour faire la tâche.

+1

même problème ici

même problème ici,

et j'essaie d'imprimer quelque chose à l'intérieur du rappel, il semble que le rappel n'a pas été appelé, mais le RabbitMQ fonctionne (fonctionne bien lorsque je déclenche une tâche dans le code)

@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)

J'utilise Celery config celery.conf.beat_schedule au lieu de dynamiquement add_periodic_task pour résoudre ce problème, car je n'ai pas à définir le calendrier de manière dynamique, mais je ne sais toujours pas pourquoi ce problème se produit.

J'ai parcouru la bibliothèque et j'ai trouvé que mon écouteur de signal était en cours de création / d'attachement après le déclenchement du signal on_after_configure . (Je plaçais mon auditeur de signal dans app/tasks.py et cela ne fonctionnait pas.)

J'ai pensé que le signal prêt pour l'application de Django s'exécuterait probablement après la configuration de Celery et cela fonctionne bien pour moi jusqu'à présent.

REMARQUE: Je ne suis pas sûr de ce que la configuration de céleri implique réellement et s'il est possible que app.ready puisse se déclencher avant que Celery ne soit configuré ... cependant, je pense que cela générerait au moins une erreur d'exécution.

Exemple de code de mon 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'
            )

Notez que vous devez également pointer INSTALLED_APPS pour utiliser votre nouveau AppConfig en settings.py :

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

Une bonne approche ou un correctif serait probablement d'écrire un nouveau décorateur qui 1) vérifie si Celery est déjà configuré et si tel est le cas, s'exécute immédiatement et 2) si Celery n'est pas configuré, ajoute l'auditeur en utilisant le @celery.on_after_configure.connect .

Dans l'état actuel des choses, les documents sont problématiques car nous sommes nombreux à avoir rencontré ce problème.

CCing @rustanacexd @viennadd juste pour que vous puissiez essayer ce correctif si vous avez encore besoin de planifier dynamiquement des tâches?

En mettant mes deux cents là-bas, j'ai été mordu et j'ai fini par devoir réorganiser certaines de mes tâches. Nous avons environ 8 tâches planifiées qui sont censées se déclencher, cependant, j'ai remarqué que ce qui suit se produirait:

Exemple:

@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')

Les commander comme ceci signifie que do_thing_a ne se déclencherait jamais, car il serait écrasé par do_thing_b . À l'origine, ils étaient tous les deux réglés sur 5 , bien qu'un seul tirait (je crois que dans ce cas, il aurait été B car il a été ajouté en premier). Ensuite, ce que j'ai fait a été de le changer en décimal et de le compenser de .1 pour voir si cela résoudrait le problème. Pas de dé. Ensuite, je leur ai ordonné que l'inférieur tire en premier et le supérieur tire en second et cela a fini par le réparer. C'est à dire:

@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')

Nous utilisons également des crontab() s, bien que ce soit une sorte de mystère pour fonctionner comme un travail et d'autres pas, je soupçonne que c'est le même problème que ci-dessus. Je n'ai pas complètement joué avec, car ces intervalles sont généralement définis pour se produire toutes les X heures / jours, alors j'oublie généralement qu'ils existent.

Peut-être que ce genre de comportement est mentionné dans la documentation, ou je vais dans un terrier différent, bien que ce comportement n'ait pas beaucoup de sens. Pour référence, nous utilisons Redis au lieu de RMQ et céleri 4.1.0.

J'ai pu faire ce travail. Vérifiez ma réponse ici:

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

@ prasanna-balaraman Cela semble fonctionner, merci pour la suggestion!

Même problème pour moi: je vais tester une autre solution: https://stackoverflow.com/a/41119054/6149867

fermeture. s'il apparaît toujours et que quelqu'un a des suggestions de code ou de documentation, n'hésitez pas à envoyer un pr faisant référence à ce problème.

Il m'a fallu un certain temps pour réaliser que s'il y a une exception levée dans setup_periodic_tasks, elle sera silencieusement supprimée.

La fonction est appelée ici: https://github.com/celery/celery/blob/master/celery/app/base.py#L950

En cas de problème, l'exception n'est enregistrée que dans les réponses, pas de ré-augmentation ou de journal:
https://github.com/celery/celery/blob/master/celery/utils/dispatch/signal.py#L276

Ma suggestion est donc de garder setup_periodic_tasks aussi simple que possible.
J'espère que cela t'aides!

@ chiang831 avez-vous des suggestions pour l'améliorer? si c'est le cas, veuillez envoyer un pr ou ouvrir une discussion sur la liste de diffusion des utilisateurs de céleri

Les définir dans on_after_finalize est ce qui a fonctionné pour moi (application de céleri non-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))

Je viens de rencontrer cela et aucune des solutions précédentes n'a fonctionné pour moi. Les scénarios exacts qui en sont la cause sont déroutants et dépendent du comportement de ref-counting / gc et des durées de vie exactes de vos fonctions décorées.

Signal.connect par défaut ne contient qu'une faible référence au gestionnaire de signaux. Cela a du sens pour d'autres cas d'utilisation de l'objet Signal (un objet de courte durée qui câble les signaux ne devrait pas être maintenu vivant par ses gestionnaires de signaux), mais est très surprenant dans ce cas.

Mon cas d'utilisation spécifique était un décorateur pour faciliter l'ajout de nouvelles tâches périodiques:

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

Le correctif consiste à demander explicitement une référence forte:

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)

L'exemple de la documentation ne fonctionne que si votre fonction décorée est à la portée du module ou de la classe, auquel cas le module ou la classe continue à contenir une référence forte à la fonction. Sinon, la seule référence forte mourra à la fin de la portée dans laquelle elle est définie.

Je recommande de changer les documents pour passer weak=False , ce qui devrait fonctionner dans les cas énumérés ci-dessus. Je n'ai cependant pas testé cela explicitement dans un contexte Django.

Pour exécuter des tâches périodiques, vous devez également appeler le planificateur lors du démarrage d'un worker en utilisant l'option -B :

celery -A proj worker -B

Lorsque vous utilisez du céleri dans les applications django, où les tâches sont détectées automatiquement à partir des applications, vous devez utiliser le signal on_after_finalize au lieu de on_after_configure .

Les références:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

Mon processus de python -m celery -A app_name worker -l info --autoscale=20,5 -BE bloqué à la fin de app_name.celery.py lors de l'utilisation de on_after_finalize .

La même configuration avec @ liuallen1981 et le même problème. Quelqu'un comprend ce qui se passe?. Pour l'instant je dois utiliser

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

au lieu d'utiliser une fonction setup_periodic_tasks avec le décorateur on_after_configure.connect .

Cela fonctionne pour moi à la place.

si vous essayez de résoudre ce problème, redémarrez d'abord votre moteur Docker, cela peut signifier un bogue système

devrions-nous fermer ce problème comme n'étant pas un bogue?

@auvipy pas sûr. On dirait que c'est un bug du céleri

C'est un bug que nous devons corriger.

Cette page vous a été utile?
0 / 5 - 0 notes