在django

时间:2016-12-13 10:40:15

标签: python django celery

对于那些发现celery 4.0.1文档中描述的定期任务声明难以集成到django中的人来说,这不是一个问题,而是帮助: http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html#entries

复制粘贴芹菜配置文件main_app/celery.py

from celery import Celery
from celery.schedules import crontab

app = Celery()

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')

    # Calls test('world') every 30 seconds
    sender.add_periodic_task(30.0, test.s('world'), expires=10)

    # Executes every Monday morning at 7:30 a.m.
    sender.add_periodic_task(
        crontab(hour=7, minute=30, day_of_week=1),
        test.s('Happy Mondays!'),
    )

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

问题

但是如果我们使用django并将我们的任务放在另一个应用程序中呢?对于芹菜4.0.1,我们不再拥有@periodic_task装饰器。那么让我们看看我们能做些什么。

第一种情况

如果您希望保持任务及其日程安排彼此接近:

main_app/some_app/tasks.py

from main_app.celery import app as celery_app

@celery_app.on_after_configure.connect
    def setup_periodic_tasks(sender, **kwargs):
        # Calls test('hello') every 10 seconds.
        sender.add_periodic_task(10.0, test.s('hello'))

@celery_app.task
def test(arg):
    print(arg)

我们可以在调试模式下运行beat

celery -A main_app beat -l debug

我们将看到没有这样的定期任务。

第二种情况

我们可以尝试在配置文件中描述所有周期性任务,如下所示:

main_app/celery.py

...
app = Celery()

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    from main_app.some_app.tasks import test
    sender.add_periodic_task(10.0, test.s('hello'))
...

结果是一样的。但是通过pdb手动调试可以看到它的行为方式不同。在第一个示例中,setup_periodic_tasks将不会触发回调。但在第二个例子中,我们将获得django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.(此异常不会被打印出来)

4 个答案:

答案 0 :(得分:22)

对于django,我们需要使用另一个信号:@celery_app.on_after_finalize.connect。它可以用于:

  • 宣布任务时间表接近app/tasks.py中的任务,因为此信号将在所有tasks.py导入和所有可能的已接收订阅者(第一种情况)之后被触发。
  • 集中日程安排声明,因为django应用程序已经初始化并准备好进口(第二种情况)

我想我应该写下最后的声明:

第一种情况

接近任务的任务计划声明:

main_app/some_app/tasks.py

from main_app.celery import app as celery_app

@celery_app.on_after_finalize.connect
    def setup_periodic_tasks(sender, **kwargs):
        # Calls test('hello') every 10 seconds.
        sender.add_periodic_task(10.0, test.s('hello'))

@celery_app.task
def test(arg):
    print(arg)

第二种情况

配置文件main_app/celery.py中的集中计划声明:

...

app = Celery()

@app.on_after_finalize.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    from main_app.some_app.tasks import test
    sender.add_periodic_task(10.0, test.s('hello'))
...

答案 1 :(得分:7)

如果目的是在tasks.py中单独维护任务逻辑,那么在from main_app.some_app.tasks import test内调用setup_periodic_tasks对我来说不起作用。有效的是:

<强> celery.py

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')

@app.task
def test(arg):
    print(arg)
    from some_app.tasks import test
    test(arg)

<强> tasks.py

@shared_task
def test(arg):
    print('world')

这导致以下输出:

[2017-10-26 22:52:42,262: INFO/MainProcess] celery@ubuntu-xenial ready.
[2017-10-26 22:52:42,263: INFO/MainProcess] Received task: main_app.celery.test[3cbdf4fa-ff63-401a-a9e4-cfd1b6bb4ad4]  
[2017-10-26 22:52:42,367: WARNING/ForkPoolWorker-2] hello
[2017-10-26 22:52:42,368: WARNING/ForkPoolWorker-2] world
[2017-10-26 22:52:42,369: INFO/ForkPoolWorker-2] Task main_app.celery.test[3cbdf4fa-ff63-401a-a9e4-cfd1b6bb4ad4] succeeded in 0.002823335991706699s: None
[2017-10-26 22:52:51,205: INFO/Beat] Scheduler: Sending due task add every 10 (main_app.celery.test)
[2017-10-26 22:52:51,207: INFO/MainProcess] Received task: main_app.celery.test[ce0f3cfc-54d5-4d74-94eb-7ced2e5a6c4b]  
[2017-10-26 22:52:51,209: WARNING/ForkPoolWorker-2] hello
[2017-10-26 22:52:51,209: WARNING/ForkPoolWorker-2] world

答案 2 :(得分:0)

我可以使用

celery.py

import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

app = Celery('mysite')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

tasks.py

from celery import current_app
app = current_app._get_current_object()

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

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

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

身份运行工人
celery -A mysite worker --beat --scheduler django --loglevel=info

答案 3 :(得分:0)

如果要单独使用任务逻辑,请使用以下设置:

celery.py

import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') # your settings.py path

app = Celery()

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    sender.add_periodic_task(5, periodic_task.s('sms'), name='SMS Process')
    sender.add_periodic_task(60, periodic_task.s('email'), name='Email Process')


@app.task
def periodic_task(taskname):
    from myapp.tasks import sms_process, email_process

    if taskname == 'sms':
        sms_process()

    elif taskname == 'email':
        email_process()

一个名为myapp的Django应用中的示例任务:

myapp / tasks.py

def sms_process():
    print('send sms task')

def email_process():
    print('send email task')