Celery: La función add_periodic_task no activa la tarea

Creado en 13 nov. 2016  ·  33Comentarios  ·  Fuente: celery/celery

Lista de Verificación

  • [x] He incluido el resultado de celery -A proj report en la edición.
    (si no puede hacer esto, al menos especifique el Apio
    versión afectada).
  • [x] He verificado que el problema existe en la sucursal master de Apio.

pasos para reproducir

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


paso 1: rabbitmq está activo
rabbitmq 1186 1 0 Nov12? 00:00:00 / bin / sh / usr / sbin / rabbitmq-server

paso 2: ejecutar tasks.py
python tasks.py
paso 3: iniciar el trabajador de ritmo
celery -A tasks -l info beat
se está iniciando apio beat v4.0.0 (latentcall).
__ - ... __ - _
LocalTime -> 2016-11-12 17:37:58
Configuración ->
. corredor -> amqp: // invitado: ** @localhost : 5672 //
. cargador -> celery.loaders.app.AppLoader
. planificador -> celery.beat.PersistentScheduler
. db -> celerybeat-schedule
. archivo de registro -> [stderr] @% INFO
. maxinterval -> 5,00 minutos (300 s)
[2016-11-12 17: 37: 58,912: INFO / MainProcess] latido: Iniciando ...

Comportamiento esperado

Espero que el programador active la función add () cada diez segundos.

Comportamiento real

La función add () no se activa.
No veo ninguna excepción en la terminal. ¿Me pierdo algo?

Celerybeat Bug Report

Comentario más útil

Para ejecutar tareas periódicas, debe invocar también el programador al iniciar un trabajador usando la opción -B :

celery -A proj worker -B

Cuando use apio en aplicaciones de django, donde las tareas se descubren automáticamente desde las aplicaciones, debe usar la señal on_after_finalize lugar de on_after_configure .

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

Todos 33 comentarios

Yo tuve el mismo problema :(

Tu ejemplo me funciona bien.

NOTA: Su manejador de señales debe aceptar ** kwargs, si no lo hace, será un error en el futuro.

Usando tu ejemplo

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

Comienzo el servicio beat de la siguiente manera:

$ 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

Hola, tengo el mismo problema. Pero trato de iniciar el apio mediante programación en un hilo. tal vez sea la causa.

Este es mi hilo:

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

Y el main.py para lanzar este

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

La salida de mi consola es

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

¿Olvidé una opción? Puedo ver que tiene un "programador" configurado en su salida @ask

Gracias de antemano por cualquier ayuda.

La misma configuración con @ liuallen1981 y el mismo problema. ¿Alguien se da cuenta de lo que está pasando ?. Por ahora tengo que usar

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

en lugar de usar una función setup_periodic_tasks con on_after_configure.connect decorador.

+1 También tengo este problema.

+1 También tengo este problema.
Apio versión 4.0.2 (latentcall)

+1 También tengo este problema.

+1 También tengo este problema. Seguí y probé con el obtuve el mismo resultado que con mi propio código.

Apio: 4.0.2

Para ejecutar tareas periódicas, debe invocar también el programador al iniciar un trabajador usando la opción -B :

celery -A proj worker -B

Cuando use apio en aplicaciones de django, donde las tareas se descubren automáticamente desde las aplicaciones, debe usar la señal on_after_finalize lugar de on_after_configure .

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

-B no es para producción y simplemente inicia el programador Beats que al menos en mi caso ya se está ejecutando.

+1 tiene el mismo problema con Apio (4.0.2)

El mismo problema aquí ...

usted acaba de iniciar un servicio de ritmo, también debe iniciar un trabajador para hacer la tarea.

+1

mismo problema aquí

mismo problema aquí,

e intento imprimir algo dentro de la devolución de llamada, parece que no se ha llamado a la devolución de llamada, pero RabbitMQ está funcionando (funciona bien cuando activo una tarea en el código)

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

Utilizo la configuración de Apio celery.conf.beat_schedule lugar de dinámicamente add_periodic_task para resolver esto, ya que no tengo que establecer el horario dinámicamente, pero aún no sé por qué ocurre este problema.

Pasé por la biblioteca y descubrí que mi oyente de señal se estaba creando / adjuntando después de que se disparara la señal on_after_configure . (Estaba colocando mi oyente de señal en app/tasks.py y no funcionaba).

Razoné que la señal de preparación de la aplicación de Django probablemente se ejecutaría después de la configuración de Celery y hasta ahora me está funcionando bien.

NOTA: No estoy seguro de lo que realmente implica la configuración de apio y si es posible que app.ready se active antes de que se configure Apio ... sin embargo, espero que al menos arroje un error de tiempo de ejecución.

Código de muestra de mi 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'
            )

Tenga en cuenta que también debe señalar INSTALLED_APPS para usar su nuevo AppConfig en settings.py :

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

Un buen enfoque o solución probablemente sería escribir un nuevo decorador que 1) verifique si Celery ya está configurado y, si es así, se ejecuta inmediatamente y 2) si Celery no está configurado, agregue el oyente usando @celery.on_after_configure.connect .

Tal como está, los documentos son problemáticos ya que muchos de nosotros nos encontramos con este problema.

¿ Enviar un CC a @viennadd solo para poder probar esta solución si aún necesita programar tareas dinámicamente?

Poniendo mis dos centavos, me mordió esto y terminé teniendo que reordenar algunas de mis tareas. Tenemos alrededor de 8 tareas programadas que se supone deben activarse, sin embargo, noté que sucedería lo siguiente:

Ejemplo:

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

Ordenarlos de esta manera significa que do_thing_a nunca se activará, ya que se sobrescribirá con do_thing_b . Originalmente, ambos estaban configurados en 5 , aunque solo uno se dispararía (creo que en este caso habría sido B ya que se agregó primero). A continuación, lo que hice fue cambiarlo a decimal y compensarlo con .1 para ver si eso lo arreglaría. No dados. Luego les ordené que el inferior disparara primero y el superior disparara segundo y eso terminó por arreglarlo. Es decir:

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

También estamos usando algunos crontab() s, aunque es una especie de misterio que se ejecuten, ya que algunos funcionan y otros no, sospecho que es el mismo problema que el anterior. No he jugado completamente con eso, ya que esos intervalos generalmente se establecen para que ocurran cada X horas / días, por lo que generalmente olvido que existen.

Tal vez este tipo de comportamiento se menciona en la documentación, o voy a pasar por una madriguera diferente, aunque este comportamiento no tiene mucho sentido. Como referencia, estamos usando Redis en lugar de RMQ y apio 4.1.0.

Pude hacer que esto funcionara. Mira mi respuesta aquí:

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

@ prasanna-balaraman Eso parece funcionar, ¡gracias por la sugerencia!

El mismo problema para mí: probaré la otra solución: https://stackoverflow.com/a/41119054/6149867

clausura. si todavía aparece y alguien tiene algún código o sugerencia de documentos, no dude en enviar una referencia a este problema.

Me tomó un tiempo darme cuenta de que si se genera alguna excepción en setup_periodic_tasks, se suprimirá silenciosamente.

La función se llama aquí: https://github.com/celery/celery/blob/master/celery/app/base.py#L950

Si algo sale mal, la excepción solo se guarda en las respuestas, no se vuelve a generar ni se registra:
https://github.com/celery/celery/blob/master/celery/utils/dispatch/signal.py#L276

Entonces, mi sugerencia es mantener setup_periodic_tasks lo más simple posible.
¡Espero que esto ayude!

@ chiang831 ¿tiene alguna sugerencia para mejorarlo? si es así, por favor envíe un PR o abra una discusión en la lista de correo de usuarios de apio

Definirlos en on_after_finalize es lo que funcionó para mí (aplicación de apio que no es de 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))

Me encontré con esto y ninguna de las soluciones anteriores funcionó para mí. Los escenarios exactos que causan esto son confusos y dependen del comportamiento del recuento de referencias / gc y la duración exacta de sus funciones decoradas.

Signal.connect por defecto solo tiene una referencia débil al manejador de señales. Esto tiene sentido para otros casos de uso del objeto Signal (un objeto de corta duración que conecta las señales no deben mantenerse vivas por sus manejadores de señales), pero es muy sorprendente en este caso.

Mi caso de uso específico fue un decorador para facilitar la adición de nuevas tareas periódicas:

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

La solución es pedir explícitamente una referencia sólida:

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)

El ejemplo en los documentos solo funciona si su función decorada está en el ámbito del módulo o de la clase, en cuyo caso el módulo o la clase continúa teniendo una fuerte referencia a la función. De lo contrario, la única referencia sólida morirá al final del alcance en el que está definida.

Recomiendo cambiar los documentos para pasar weak=False , que debería funcionar en los casos enumerados anteriormente. Sin embargo, no he probado esto explícitamente en un contexto de Django.

Para ejecutar tareas periódicas, debe invocar también el programador al iniciar un trabajador usando la opción -B :

celery -A proj worker -B

Cuando use apio en aplicaciones de django, donde las tareas se descubren automáticamente desde las aplicaciones, debe usar la señal on_after_finalize lugar de on_after_configure .

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

Mi proceso de python -m celery -A app_name worker -l info --autoscale=20,5 -BE bloqueó al final de app_name.celery.py cuando uso on_after_finalize .

La misma configuración con @ liuallen1981 y el mismo problema. ¿Alguien se da cuenta de lo que está pasando ?. Por ahora tengo que usar

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

en lugar de usar una función setup_periodic_tasks con on_after_configure.connect decorador.

En cambio, esto funciona para mí.

Si está tratando de resolver este problema, reinicie primero el motor de la ventana acoplable, puede ser una señal de error del sistema.

¿Deberíamos cerrar este problema como si no fuera un error?

@auvipy no estoy seguro. Parece que es un bicho de apio

Es un error que debemos corregir.

¿Fue útil esta página
0 / 5 - 0 calificaciones