在 Django 项目中使用 Celery

Celery 先前的版本需要额外安装一个库才能与 Django 集成,但是自3.1版本开始,再也不需要了。现在 Celery 直接支持 Django 了,本文提供一个比较基本的方法将 Celery 集成到 Django 项目中。你将使用与非 Django 用户同样的API,所以在阅读本文之前最好看一下Celery 初步。当你完成一个可以正常运行的例子后,再看看Celery 进阶。

如果未安装django和celery可查看一下官网文档安装以后再执行本文章以下操作。

首先为了在 Django 项目中使用 Celery,必须先定义一个 Celery 实例(也叫做 app)。

假如 Django 项目布局是这样的:

- myproject/
  - myproject/__init__.py
  - myproject/settings.py
  - myproject/urls.py
  - myproject/wsgi.py
- manage.py

那么,推荐的做法是创建一个新的 myproject/myproject/celery.py 模块,然后在这个模块中定义 Celery 实例。
file: myproject/myproject/celery.py

# coding:utf-8
from __future__ import absolute_import, unicode_literals

from celery import Celery
import os

import myproject.celery_config

# 获取当前文件夹名,即为该Django的项目名
project_name = os.path.split(os.path.abspath('.'))[-1]
project_settings = '%s.settings' % project_name

# 设置环境变量 主要可用于在定时任务 或者其他任务中操作数据库
os.environ.setdefault('DJANGO_SETTINGS_MODULE', project_settings)

# 实例化Celery
app = Celery(project_name)

# 使用anync_task.celery_config
app.config_from_object(myproject.celery_config)

# Celery加载所有注册的应用
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

接着,需要在 myproject/myproject/__init__.py 模块中导入这个 Celery 实例(也就是 app)。这样可以确保当 Django 启动时可以加载这个 app,并且 @shared_task 装饰器(后面会提到)也能使用这个 app.

file: myproject/myproject/__init__.py

# coding:utf-8
from __future__ import absolute_import, unicode_literals

# 引入celery对象
from .celery import app as celery_app

需要注意的是,上述项目布局示例适合大型项目。对于简单的项目,你可以在一个模块中同时定义 Celery 实例和任务,就像Celery 初步里面那样。

--------------------------------------------------

以下需要注意:

我们看看在 myproject/myproject/__init__.py 模块中到底做了什么事。首先,从 future 模块导入 absolute_import,这样,celery.py 模块就不会与 Celery 库相冲突:

	from __future__ import absolute_import

然后,为 celery 命令运行程序设置环境变量 DJANGO_SETTINGS_MODULE 的默认值:

	os.environ.setdefault('DJANGO_SETTINGS_MODULE', project_settings)

设置这个环境变量是为了让 celery 命令能找到 Django 项目。这条语句必须出现在 Celery 实例创建之前,也就是接下来要做的:

app = Celery(project_name)

这个 app 就是 Celery 实例。可以有很多 Celery 实例,但是当使用 Django 时,似乎没有必要。

我们还可以将Django的settings 文件作为 Celery 的配置来源,这样,你不必使用多个配置文件,而是直接从Django的settings 文件配置Celery; 但是如果需要,你也可以将它们分开编写

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

上面这里的大写的名称空间意味着必须以大写而不是小写来指定所有Celery配置选项,并以…开头 CELERY_,例如task_always_eager设置变为CELERY_TASK_ALWAYS_EAGER,broker_url 设置变为CELERY_BROKER_URL

可以将 settings 对象作为参数传入,但是更好的方式是使用字符串,因为当使用 Windows 系统或者 execv 时 celery worker 不需要序列化 settings 对象,该CELERY_命名空间也是可选的,但建议这样做(以防止与其他Django的设置重叠)

为了重用 Django APP,通常是在单独的 tasks.py 模块中定义所有任务。Celery 会自动发现这些模块:

app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

加上上一句后,Celery 会自动发现 Django APP 中定义的任务,前提是遵循如下 tasks.py 约定:

- app1/
    - tasks.py
    - models.py
- app2/
    - tasks.py
    - models.py

这样您就不必手动将各个模块添加到CELERY_IMPORTS设置中。
传入的 lambda 函数有如下好处:只在需要的时候才自动发现任务,以及当导入模块时不需要立即对 settings 对象求值

最后,debug_task 是一个打印本身 request 信息的任务。它使用了在 Celery 3.1引入的任务选项 bind=True,使得引用当前任务实例变得很容易。

使用 @shared_task 装饰器

你很可能在可重用的 Django APP 中编写了一些任务,但是 Django APP 不能依赖于具体的 Django 项目,所以你无法直接导入 Celery 实例。
@shared_task 装饰器能让你在没有具体的 Celery 实例时创建任务:
file: demoapp/tasks.py:

from __future__ import absolute_import
from celery import shared_task

@shared_task
def add(x, y):
    return x + y

@shared_task
def mul(x, y):
    return x * y

@shared_task
def xsum(numbers):
    return sum(numbers)

另请参阅你可以在这里找到这个 Django 示例项目的完整源码

---------------------------------------------

使用django-celery-results Django ORM/Cache 作为结果存储后端(result backend)

所述的django-celery-results这个模块可以使用任何Django的ORM,或Django的Cache框架作为结果后端。
要在项目中使用它,您需要按照以下步骤操作:
1.安装django-celery-results库:

$ pip install django-celery-results

2.添加django_celery_results到INSTALLED_APPS您的Django项目中settings.py:

# 请注意,模块名称中没有短划线,只有下划线。
INSTALLED_APPS = (
    ...,
    'django_celery_results',
)

3.通过执行数据库迁移来创建Celery数据库表:

$ python manage.py migrate django_celery_results

4.配置Celery以使用django-celery-results后端。

假设您正在使用Django settings.py来配置Celery,请添加以下设置:

CELERY_RESULT_BACKEND = 'django-db'

对于缓存后端,您可以使用:

CELERY_RESULT_BACKEND = 'django-cache'

------------------------------------------------

自己实现的配置文件

myproject/myproject/路径下新建celery.pycelery_config.py项目路径如下:

- myproject/
  - myproject/__init__.py
  - myproject/celery.py
  - myproject/celery_config.py
  - myproject/settings.py
  - myproject/urls.py
  - myproject/wsgi.py
- manage.py
其中file: myproject/myproject/celery.py
# coding:utf-8
# 防止绝对模块冲突
from __future__ import absolute_import, unicode_literals

from celery import Celery
import os
# 导入配置文件
import myproject.celery_config

# 获取当前文件夹名,即为该Django的项目名
project_name = os.path.split(os.path.abspath('.'))[-1]
# 获取django的配置文件
project_settings = '%s.settings' % project_name

# 设置celery环境变量 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', project_settings)

# 实例化Celery
app = Celery(project_name)
# 使用自定义的celery config 文件
app.config_from_object(myproject.celery_config)

# # Celery加载所有注册的应用 
# app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

这句app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)注释掉是因为在自定义配置文件中已经指定了任务模块

file: myproject/myproject/__init__.py:
# coding:utf-8
from __future__ import absolute_import, unicode_literals

# 引入celery对象
from .celery import app as celery_app

file: myproject/myproject/celery_config.py:

注意:这里是用RabbitMQ当做中间人RabbitMQ关于RabbitMQ的安装可查看上一篇文章RabbitMQ服务器的搭建

# coding:utf-8
from datetime import timedelta

# 添加celery配置
# 中间消息队列
BROKER_URL = 'amqp://guest:guest@localhost:5672//'
# BROKER_URL = 'django://'  # 用django默认数据库作为中间消息队列
# celery 用redis作为储存后端结果
CELERY_RESULT_BACKEND = 'redis://localhost:6379/1'
# celery 接受内容形式
CELERY_ACCEPT_CONTENT = ['application/json']
# celery 任务序列化
CELERY_TASK_SERIALIZER = 'json'
# celery 结果序列化
CELERY_RESULT_SERIALIZER = 'json'
# celery时间
CELERY_TIMEZONE = 'UTC'
# celery 是否忽略结果
# CELERY_IGNORE_RESULT = True

# 指定导入的任务模块 
CELERY_IMPORTS = (
    'anync_task.task_1',
    'anync_task.task_2',
    'anync_task.task_3',
)

# 定时任务配置
CELERYBEAT_SCHEDULE = {
    'add-every-30-seconds': {
         'task': 'anync_task.task_3.send_message',
         'schedule': timedelta(seconds=30),       # 每 30 秒执行一次
         'args': (11,)                           # 任务函数参数
    },
}

注意:task_3为定时任务,以上的配置文件中的CELERY_IMPORTS中的内容:
为导入那个模块中的那些任务文件,其中路径为下

- myproject/
  - anync_task/__init__.py
  - anync_task/task_1.py
  - anync_task/task_2.py
  - anync_task/task_3.py

  - myapp/__init__.py
  - myapp/admin.py
  - myapp/app.py
  - myapp/models.py
  - myapp/test.py
  - myapp/view.py

  - myproject/__init__.py
  - myproject/celery.py
  - myproject/celery_config.py
  - myproject/settings.py
  - myproject/urls.py
  - myproject/wsgi.py
- manage.py

其他文件如下:

task_1.py
# coding:utf-8
from __future__ import absolute_import
# from celery.decorators import task
from celery import task
import time
from myapp.models import Blog

@task
def sendmail(email):
    # 异步任务
    print(u'这是执行task 1')
    result = Blog.objects.all()
    print(result)
    print('start send email to %s' % email)
    time.sleep(5)  # 休息5秒
    print('success')
    return 'Hello world'
# ------------------------------------------------------------
task_2.py
# coding:utf-8
from __future__ import absolute_import
# from celery.decorators import task
from celery import task
import time
from myapp.models import Blog

@task
def send_qq(email):
    # 异步任务
    result = Blog.objects.all()
    print(u'这是执行task_2')
    print(result)
    print('start send email to %s' % email)
    time.sleep(5)  # 休息5秒
    print('success')
    return 'hello world'
# ------------------------------------------------------------
task_3.py
# coding:utf-8
from __future__ import absolute_import
# from celery.decorators import task
from celery import task
import time

@task
def send_message(email):
    # 异步任务
    print(u'这是执行task_3')

    print('start send email to %s' % email)
    time.sleep(5)  # 休息5秒
    print('success')
    return True
# ------------------------------------------------------------
model.py
# coding:utf-8
from __future__ import unicode_literals
from django.db import models


class Blog(models.Model):
    caption = models.CharField(max_length=30)

    def __unicode__(self):
        return self.caption
# ------------------------------------------------------------
view.py
# coding:utf-8
from django.shortcuts import render
from django.http import HttpResponse
from .models import Blog
from anync_task.task_1 import sendmail
from anync_task.task_2 import send_qq
import json


def home(request):
    # 耗时任务,发送邮件
    result_1 = sendmail.delay('test@test.com')
    print(result_1.result)
    print(result_1.status)
    print(result_1.successful())
    print(result_1.backend)
    result_2 = send_qq.delay('test@test.com')
    print(result_2.result)
    print(result_2.status)
    print(result_2.successful())
    print(result_2.backend)

    # 其他行为
    data = list(Blog.objects.values('caption'))
    return HttpResponse(json.dumps(data), content_type='application/json')

可以尝试一下

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值