Celery: função add_periodic_task não dispara a tarefa

Criado em 13 nov. 2016  ·  33Comentários  ·  Fonte: celery/celery

Lista de controle

  • [x] Incluí a saída de celery -A proj report na edição.
    (se você não for capaz de fazer isso, pelo menos especifique o aipo
    versão afetada).
  • [x] Eu verifiquei que o problema existe no ramo master do aipo.

Passos para reproduzir

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


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

etapa 2: execute tasks.py
python tasks.py
passo 3: iniciar o trabalhador de batida
celery -A tasks -l info beat
celery beat v4.0.0 (latentcall) está começando.
__ - ... __ - _
LocalTime -> 2016-11-12 17:37:58
Configuração ->
. corretor -> amqp: // convidado: ** @localhost : 5672 //
. loader -> celery.loaders.app.AppLoader
. planejador -> celery.beat.PersistentScheduler
. db -> agendamento de celerybeat
. arquivo de log -> [stderr] @% INFO
. maxinterval -> 5,00 minutos (300s)
[2016-11-12 17: 37: 58,912: INFO / MainProcess] batida: Iniciando ...

Comportamento esperado

Espero que o planejador acione a função add () a cada dez segundos.

Comportamento real

A função add () não é acionada.
Não vejo nenhuma exceção no terminal. Eu sinto falta de alguma coisa?

Celerybeat Bug Report

Comentários muito úteis

Para executar tarefas periódicas, você deve invocar também o agendador ao iniciar um trabalhador usando a opção -B :

celery -A proj worker -B

Ao usar o aipo em aplicativos django, onde as tarefas são descobertas automaticamente de aplicativos, você precisa usar o sinal on_after_finalize vez de on_after_configure .

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

Todos 33 comentários

Eu tive o mesmo problema :(

Seu exemplo funciona bem para mim.

NOTA: Seu manipulador de sinais precisa aceitar ** kwargs, deixar de fazer isso será um erro no futuro.

Usando o seu exemplo

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

Eu começo o serviço de batida da seguinte maneira:

$ 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

Oi, eu tenho o mesmo problema. Mas eu tento iniciar o aipo programaticamente em um segmento. talvez seja a causa.

Este é o meu tópico:

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

E o main.py para lançar este

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

A saída do meu console é

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

Eu esqueci uma opção? Eu posso ver que você tem um "planejador" definido em sua saída @ask

Agradecemos antecipadamente por qualquer ajuda.

A mesma configuração com @ liuallen1981 e o mesmo problema. Alguém descobriu o que está acontecendo? Por enquanto eu tenho que usar

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

em vez de usar uma função setup_periodic_tasks com on_after_configure.connect decorador.

1 Também tendo esse problema.

1 Também tendo esse problema.
Celery versão 4.0.2 (latentcall)

1 Também tendo esse problema.

1 Também tendo esse problema. Continuei e testei com o código de obtive o mesmo resultado do meu próprio código.

Aipo: 4.0.2

Para executar tarefas periódicas, você deve invocar também o agendador ao iniciar um trabalhador usando a opção -B :

celery -A proj worker -B

Ao usar o aipo em aplicativos django, onde as tarefas são descobertas automaticamente de aplicativos, você precisa usar o sinal on_after_finalize vez de on_after_configure .

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

-B não é para produção e simplesmente inicia o agendador do Beats que pelo menos no meu caso já está em execução.

+1 tendo o mesmo problema com aipo (4.0.2)

O mesmo problema aqui ....

você acabou de iniciar um serviço de batida, também deve iniciar um trabalhador para fazer a tarefa.

+1

mesmo problema aqui

mesmo problema aqui,

e tento imprimir algo dentro do callback, parece que o callback não foi chamado, mas o RabbitMQ está funcionando (funciona bem quando eu acionar uma tarefa no 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)

Eu uso a configuração do Celery celery.conf.beat_schedule vez de add_periodic_task dinamicamente para resolver isso, já que não tenho que definir a programação dinamicamente, mas ainda não sei por que esse problema está acontecendo.

Percorri a biblioteca e descobri que meu ouvinte de sinal estava sendo criado / conectado depois que o sinal on_after_configure foi disparado. (Eu estava colocando meu ouvinte de sinal em app/tasks.py e não estava funcionando.)

Concluí que o sinal de pronto do aplicativo do Django provavelmente seria executado após a configuração do Celery e está funcionando bem para mim até agora.

NOTA: Não tenho certeza do que a configuração do aipo realmente envolve e se é possível que app.ready possa ser acionado antes que o Celery seja configurado ... no entanto, espero que pelo menos geraria um erro de execução.

Amostra de código do meu 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'
            )

Observe que você também precisa apontar INSTALLED_APPS para usar seu novo AppConfig em settings.py :

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

Uma boa abordagem ou correção provavelmente seria escrever um novo decorador que 1) verifique se o Celery já está configurado e, se estiver, é executado imediatamente e 2) se o Celery não estiver configurado, adiciona o ouvinte usando @celery.on_after_configure.connect .

Do jeito que está, os documentos são problemáticos, pois muitos de nós enfrentamos esse problema.

Copiar @rustanacexd @viennadd apenas para tentar essa correção se ainda precisar agendar tarefas dinamicamente?

Colocando meus dois centavos lá fora, eu fui um pouco com isso e acabei tendo que reordenar algumas das minhas tarefas. Temos cerca de 8 tarefas agendadas que devem ser acionadas, no entanto, percebi que aconteceria o seguinte:

Exemplo:

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

Ordená-los desta forma significa que do_thing_a nunca será acionado, pois será sobrescrito por do_thing_b . Originalmente, ambos foram definidos como 5 , embora apenas um fosse disparar (acredito que neste caso teria sido B, pois foi adicionado primeiro). Em seguida, o que fiz foi alterá-lo para um decimal e compensá-lo em .1 para ver se isso resolveria o problema. Sem dados. Então eu ordenei que o inferior disparasse primeiro e o superior disparasse depois e isso acabou consertando. Ou seja:

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

Também estamos usando alguns crontab() s, embora sejam um mistério para serem executados, pois alguns funcionam e outros não, suspeito que seja o mesmo problema acima. Eu não brinquei completamente com isso, já que esses intervalos são geralmente configurados para ocorrer a cada X horas / dias, então eu geralmente esqueço que eles existem.

Talvez esse tipo de comportamento seja mencionado na documentação, ou estou indo por uma toca de coelho diferente, embora esse comportamento não faça muito sentido. Para referência, estamos usando Redis em vez de RMQ e aipo 4.1.0.

Eu fui capaz de fazer isso funcionar. Verifique minha resposta aqui:

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

@prasanna-balaraman Isso parece funcionar, obrigado pela sugestão!

O mesmo problema para mim: vou testar a outra solução: https://stackoverflow.com/a/41119054/6149867

fechando. se ainda aparecer e alguém tiver qualquer sugestão de código ou docs, sinta-se à vontade para enviar um pr referenciando este problema.

Demorei um pouco para perceber que, se houver alguma exceção levantada em setup_periodic_tasks, ela será silenciosamente suprimida.

A função é chamada aqui: https://github.com/celery/celery/blob/master/celery/app/base.py#L950

Se algo der errado, a exceção é salva apenas nas respostas, sem novo aumento ou registro:
https://github.com/celery/celery/blob/master/celery/utils/dispatch/signal.py#L276

Portanto, minha sugestão é manter setup_periodic_tasks o mais simples possível.
Espero que isto ajude!

@ chiang831 você tem alguma sugestão para melhorá-lo? se sim, por favor, envie um relatório ou abra uma discussão na lista de discussão dos usuários do aipo

Defini-los em on_after_finalize funcionou para mim (aplicativo de aipo não 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))

Acabei de encontrar isso e nenhuma das soluções anteriores funcionou para mim. Os cenários exatos que causam isso são confusos e dependem do comportamento da contagem de referência / gc e das vidas úteis exatas de suas funções decoradas.

Signal.connect por padrão contém apenas uma referência fraca para o manipulador de sinal. Isso faz sentido para outros casos de uso do objeto Signal (um objeto de curta duração que conecta sinais não deve ser mantido vivo por seus manipuladores de sinal), mas é muito surpreendente neste caso.

Meu caso de uso específico foi um decorador para facilitar a adição de novas tarefas 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

A solução é pedir explicitamente uma referência 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)

O exemplo na documentação funciona se sua função decorada estiver no escopo do módulo ou da classe, caso em que o módulo ou classe continua a conter uma referência forte à função. Caso contrário, a única referência forte morrerá no final do escopo em que está definida.

Eu recomendo alterar os documentos para passar weak=False , o que deve funcionar nos casos listados acima. No entanto, não testei explicitamente isso em um contexto Django.

Para executar tarefas periódicas, você deve invocar também o agendador ao iniciar um trabalhador usando a opção -B :

celery -A proj worker -B

Ao usar o aipo em aplicativos django, onde as tarefas são descobertas automaticamente de aplicativos, você precisa usar o sinal on_after_finalize vez de on_after_configure .

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

Meu processo de python -m celery -A app_name worker -l info --autoscale=20,5 -BE bloqueado no final de app_name.celery.py quando uso on_after_finalize .

A mesma configuração com @ liuallen1981 e o mesmo problema. Alguém descobriu o que está acontecendo? Por enquanto eu tenho que usar

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

em vez de usar uma função setup_periodic_tasks com on_after_configure.connect decorador.

Isso funciona para mim em vez disso.

se você está tentando resolver esse problema, reinicie o motor do docker primeiro, pode ser um bug do sistema de sinais

devemos encerrar este problema como não um bug?

@auvipy não tenho certeza. Parece que é um inseto do aipo

É um bug que devemos consertar.

Esta página foi útil?
0 / 5 - 0 avaliações