Celery: Die Funktion add_periodic_task löst die Aufgabe nicht aus

Erstellt am 13. Nov. 2016  ·  33Kommentare  ·  Quelle: celery/celery

Checkliste

  • [x] Ich habe die Ausgabe von celery -A proj report in die Ausgabe aufgenommen.
    (Wenn Sie dies nicht können, geben Sie mindestens den Sellerie an
    Version betroffen).
  • [x] Ich habe überprüft, ob das Problem gegen den Zweig master von Celery besteht.

Schritte zum Reproduzieren

task.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()


Schritt 1: Rabbitmq ist aktiv
rabbitmq 1186 1 0 Nov12? 00:00:00 / bin / sh / usr / sbin / rabbitmq-server

Schritt 2: Führen Sie task.py aus
python tasks.py
Schritt 3: Starten Sie Beat Worker
celery -A tasks -l info beat
Sellerie Beat v4.0.0 (latentcall) startet.
__ - ... __ - _
LocalTime -> 2016-11-12 17:37:58
Konfiguration ->
. Broker -> amqp: // guest: ** @localhost : 5672 //
. loader -> celery.loaders.app.AppLoader
. Scheduler -> celery.beat.PersistentScheduler
. db -> Sellerie-Beat-Zeitplan
. Protokolldatei -> [stderr] @% INFO
. maximales Intervall -> 5,00 Minuten (300 s)
[2016-11-12 17: 37: 58,912: INFO / MainProcess] Beat: Start ...

Erwartetes Verhalten

Ich erwarte, dass der Scheduler alle zehn Sekunden die Funktion add () auslöst.

Tatsächliches Verhalten

Die Funktion add () wird nicht ausgelöst.
Ich sehe keine Ausnahme im Terminal. Vermisse ich etwas

Celerybeat Bug Report

Hilfreichster Kommentar

Um periodische Aufgaben auszuführen, müssen Sie auch den Scheduler aufrufen, wenn Sie einen Worker mit der Option -B starten:

celery -A proj worker -B

Wenn Sie Sellerie in Django-Anwendungen verwenden, in denen Aufgaben automatisch aus Apps erkannt werden, müssen Sie das Signal on_after_finalize anstelle von on_after_configure .

Verweise:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

Alle 33 Kommentare

Ich hatte das gleiche Problem :(

Ihr Beispiel funktioniert gut für mich.

HINWEIS: Ihr Signalhandler muss ** kwargs akzeptieren. Andernfalls wird dies in Zukunft ein Fehler sein.

Anhand Ihres Beispiels

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

Ich starte den Beat-Service wie folgt:

$ 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

Hallo, ich habe das gleiche Problem. Aber ich versuche, Sellerie programmgesteuert in einem Thread zu starten. Vielleicht ist es die Ursache.

Das ist mein Thread:

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

Und die main.py, um dies zu starten

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

Meine Konsolenausgabe ist

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

Vergesse ich eine Option? Ich kann sehen, dass in Ihrer Ausgabe @ask ein "Scheduler" festgelegt ist

Vielen Dank im Voraus für jede Hilfe.

Dieselbe Konfiguration mit

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

anstatt eine setup_periodic_tasks -Funktion mit on_after_configure.connect Decorator zu verwenden.

+1 Auch mit diesem Problem.

+1 Auch mit diesem Problem.
Sellerie Version 4.0.2 (latentcall)

+1 Auch mit diesem Problem.

+1 Auch mit diesem Problem. Ging weiter und testete mit dem Code von @ liuallen1981 und erhielt das gleiche Ergebnis wie mit meinem eigenen Code.

Sellerie: 4.0.2

Um periodische Aufgaben auszuführen, müssen Sie auch den Scheduler aufrufen, wenn Sie einen Worker mit der Option -B starten:

celery -A proj worker -B

Wenn Sie Sellerie in Django-Anwendungen verwenden, in denen Aufgaben automatisch aus Apps erkannt werden, müssen Sie das Signal on_after_finalize anstelle von on_after_configure .

Verweise:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

-B ist nicht für die Produktion und startet einfach den Beats-Scheduler, der zumindest in meinem Fall bereits läuft.

+1 mit dem gleichen Problem mit Sellerie (4.0.2)

Gleiches Problem hier ....

Wenn Sie nur einen Beat-Service starten, sollten Sie auch einen Mitarbeiter starten, der die Aufgabe erledigt.

+1

das gleiche Problem hier

das gleiche Problem hier,

und ich versuche, etwas innerhalb des Rückrufs zu drucken, anscheinend wurde der Rückruf nicht aufgerufen, aber der RabbitMQ funktioniert (funktioniert gut, wenn ich eine Aufgabe im Code auslöse).

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

Ich verwende die Selleriekonfiguration celery.conf.beat_schedule anstelle von dynamisch add_periodic_task , um dies zu lösen, da ich den Zeitplan nicht dynamisch festlegen muss, aber immer noch nicht weiß, warum dieses Problem auftritt.

Ich ging durch die Bibliothek und stellte fest, dass mein Signal-Listener erstellt / angehängt wurde, nachdem das Signal on_after_configure ausgelöst wurde. (Ich habe meinen Signal-Listener in app/tasks.py und es hat nicht funktioniert.)

Ich bin zu dem Schluss gekommen, dass das App-Ready-Signal von Django wahrscheinlich nach der Sellerie-Konfiguration ausgeführt wird und es für mich bisher gut funktioniert.

HINWEIS: Ich bin nicht sicher, welche Selleriekonfiguration tatsächlich beinhaltet und ob es möglich ist, dass app.ready ausgelöst werden kann, bevor Sellerie konfiguriert wird. Ich gehe jedoch davon aus, dass dies zumindest einen Laufzeitfehler auslösen würde.

Beispielcode aus meinem 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'
            )

Beachten Sie, dass Sie auch auf INSTALLED_APPS verweisen müssen, um Ihr neues AppConfig in settings.py :

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

Ein guter Ansatz oder eine gute Lösung wäre wahrscheinlich, einen neuen Dekorateur zu schreiben, der 1) prüft, ob Sellerie bereits konfiguriert ist, und wenn ja, sofort ausgeführt wird und 2) wenn Sellerie nicht konfiguriert ist, den Listener mit @celery.on_after_configure.connect hinzufügt.

Derzeit sind die Dokumente problematisch, da so viele von uns auf dieses Problem gestoßen sind.

CCing @rustanacexd @viennadd, damit Sie dieses Update ausprobieren können, wenn Sie noch Aufgaben dynamisch planen müssen?

Nachdem ich meine zwei Cent da draußen hingelegt hatte, wurde ich ein bisschen davon und musste schließlich einige meiner Aufgaben neu ordnen. Wir haben ungefähr 8 geplante Aufgaben, die ausgelöst werden sollen. Ich habe jedoch festgestellt, dass Folgendes passieren würde:

Beispiel:

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

Wenn Sie sie so bestellen, bedeutet dies, dass do_thing_a niemals ausgelöst wird, da dies durch do_thing_b überschrieben wird. Ursprünglich waren beide auf 5 , obwohl nur einer feuern würde (ich glaube, in diesem Fall wäre es B gewesen, als es zuerst hinzugefügt wurde). Als nächstes habe ich es in eine Dezimalzahl geändert und um .1 zu sehen, ob dies das Problem beheben würde. Kein Würfel. Dann bestellte ich sie so, dass der niedrigere zuerst und der höhere zweitens feuerte und das endete damit, dass sie repariert wurden. Dh:

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

Wir verwenden auch einige crontab() s, obwohl diese ein Rätsel sind, um zum Laufen zu kommen, da einige arbeiten und andere nicht, ich vermute, es ist das gleiche Problem wie oben. Ich habe noch nicht vollständig damit herumgespielt, da diese Intervalle im Allgemeinen alle X Stunden / Tage auftreten, sodass ich normalerweise vergesse, dass sie existieren.

Vielleicht wird diese Art von Verhalten in der Dokumentation erwähnt, oder ich gehe in ein anderes Kaninchenloch, obwohl dieses Verhalten nicht viel Sinn macht. Als Referenz verwenden wir Redis anstelle von RMQ und Sellerie 4.1.0.

Ich konnte diese Arbeit machen. Überprüfen Sie meine Antwort hier:

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

@ prasanna-balaraman Das scheint zu funktionieren, danke für den Vorschlag!

Gleiches Problem für mich: Ich werde die andere Lösung testen: https://stackoverflow.com/a/41119054/6149867

Schließen. Wenn es immer noch angezeigt wird und jemand Code- oder Dokumentvorschläge hat, senden Sie bitte einen PR, der auf dieses Problem verweist.

Es hat eine Weile gedauert, bis mir klar wurde, dass eine Ausnahme, die in setup_periodic_tasks ausgelöst wird, stillschweigend unterdrückt wird.

Die Funktion wird hier aufgerufen: https://github.com/celery/celery/blob/master/celery/app/base.py#L950

Wenn etwas schief geht, wird die Ausnahme nur in Antworten gespeichert, nicht erneut ausgelöst oder protokolliert:
https://github.com/celery/celery/blob/master/celery/utils/dispatch/signal.py#L276

Mein Vorschlag ist daher, setup_periodic_tasks so einfach wie möglich zu halten.
Hoffe das hilft!

@ chiang831 hast du irgendwelche vorschläge um es zu verbessern? Wenn ja, senden Sie bitte einen PR oder eröffnen Sie eine Diskussion über die Mailingliste der Sellerie-Benutzer

Die Definition in on_after_finalize hat bei mir funktioniert (Nicht-Django-Sellerie-App).

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

Ich bin gerade darauf gestoßen und keine der vorherigen Lösungen hat für mich funktioniert. Die genauen Szenarien, die dies verursachen, sind verwirrend und hängen vom Verhalten der Nachzählung / gc und der genauen Lebensdauer Ihrer dekorierten Funktionen ab.

Signal.connect enthält standardmäßig nur einen schwachen Verweis auf den Signalhandler. Dies ist für andere Anwendungsfälle des Signalobjekts sinnvoll (ein kurzlebiges Objekt, bei dem verdrahtete Signale von seinen Signalhandlern nicht am Leben gehalten werden sollten), ist jedoch in diesem Fall sehr überraschend.

Mein spezieller Anwendungsfall war ein Dekorateur, um das Hinzufügen neuer periodischer Aufgaben zu vereinfachen:

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

Die Lösung besteht darin, explizit nach einer starken Referenz zu fragen:

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)

Das Beispiel in den Dokumenten funktioniert nur, wenn sich Ihre dekorierte Funktion im Modul- oder Klassenbereich befindet. In diesem Fall enthält das Modul oder die Klasse weiterhin einen starken Verweis auf die Funktion. Andernfalls stirbt die einzige starke Referenz am Ende des Bereichs, in dem sie definiert ist.

Ich empfehle, die Dokumente so zu ändern, dass weak=False , was in den oben aufgeführten Fällen funktionieren sollte. Ich habe dies jedoch nicht explizit in einem Django-Kontext getestet.

Um periodische Aufgaben auszuführen, müssen Sie auch den Scheduler aufrufen, wenn Sie einen Worker mit der Option -B starten:

celery -A proj worker -B

Wenn Sie Sellerie in Django-Anwendungen verwenden, in denen Aufgaben automatisch aus Apps erkannt werden, müssen Sie das Signal on_after_finalize anstelle von on_after_configure .

Verweise:
http://stackoverflow.com/questions/40712678/setup-periodic-task/40727526
http://stackoverflow.com/questions/41119053/connect-new-celery-periodic-task-in-django

Mein Prozess von python -m celery -A app_name worker -l info --autoscale=20,5 -BE am Ende von app_name.celery.py python -m celery -A app_name worker -l info --autoscale=20,5 -BE blockiert, wenn on_after_finalize .

Dieselbe Konfiguration mit

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

anstatt eine setup_periodic_tasks -Funktion mit on_after_configure.connect Decorator zu verwenden.

Das funktioniert stattdessen bei mir.

Wenn Sie versuchen, dieses Problem zu lösen, starten Sie zuerst Ihre Docker-Engine neu. Dies kann auf einen Systemfehler hinweisen

Sollen wir dieses Problem als keinen Fehler schließen?

@auvipy nicht sicher. Sieht aus wie es ein Sellerie-Käfer ist

Es ist ein Fehler, den wir beheben müssen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen