Django 进阶之 celery

本文介绍了将 Celery 集成到 Django 项目中,实现异步任务和定时任务处理。阐述了 Celery 架构,包括消息中间件、任务执行单元和结果存储。详细说明了 Celery 安装配置、Django 中配置、服务启动测试、邮件发送服务及 flower 服务启动,还给出读书网站抢读功能案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Django 集成 Celery 到项目

本节将 celery 集成到 Django 项目中,实现异步任务处理和定时任务处理

Celery 工作流程

Django 进阶之 celery

Celery 的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。

消息中间件

Celery 本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis, MongoDB (experimental), Amazon SQS (experimental),CouchDB (experimental), SQLAlchemy (experimental),Django ORM (experimental), IronMQ

任务执行单元

Worker 是 Celery 提供的任务执行的单元,worker 并发的运行在分布式的系统节点中。

任务结果存储

Task result store 用来存储 Worker 执行的任务的结果,Celery 支持以不同方式存储任务的结果,包括 AMQP, Redis,memcached, MongoDB,SQLAlchemy, Django ORM,Apache Cassandra, IronCache

1、Celery 安装与配置

在虚拟环境中安装:

pip install django-celery==3.2.2

pip install django-redis

pip install flower # celery 的web管理平台(异步任务可视化)

查看集成到 Django 中的 celery 版本, pip freeze

celery==3.1.26.post2 django-celery==3.2.2 flower==0.9.2

启动 redis 服务, 端口假设为 6379

发现 pip 安装比较慢的情况

pip install pillow -i https://pypi.douban.com/simple

2、Django 中配置

(1)在主工程的配置文件settings.py 中应用注册表INSTALLED_APPS中加入 djcelery

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'art',
    'xadmin',
    'crispy_forms',
    'DjangoUeditor',
    'djcelery',       #加入djcelery
]

(2) 在 settings.py 中加入 celery 配置信息

#############################
# celery 配置信息 start
#############################
import djcelery
djcelery.setup_loader()
BROKER_URL = 'redis://127.0.0.1:6379/1'
CELERY_IMPORTS = ('art.tasks')
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler' 
from celery.schedules import crontab
from celery.schedules import timedelta

CELERYBEAT_SCHEDULE = {    #定时器策略
    #定时任务一: 每隔30s运行一次
    u'测试定时器1': {
        "task": "art.tasks.tsend_email",
        #"schedule": crontab(minute='*/2'),  # or 'schedule':   timedelta(seconds=3),
        "schedule":timedelta(seconds=30),
        "args": (),
    },
}
#############################
# celery 配置信息 end
#############################

当 djcelery.setup_loader() 运行时,Celery 便会去查看 INSTALLD_APPS 下包含的所有 app目录中的 tasks.py 文件,找到标记为 task 的方法,将它们注册为 celery task

BROKER_URL:broker 是代理人,它负责分发任务给 worker 去执行。我使用的是 Redis 作为 broker

没有设置 CELERY_RESULT_BACKEND,默认没有配置,此时 Django 会使用默认的数据库(也是你指定的 orm 数据库)。

CELERY_IMPORTS:是导入目标任务文件

CELERYBEAT_SCHEDULER:使用了 django-celery 默认的数据库调度模型,任务执行周期都被存在默认指定的 orm 数据库中.

CELERYBEAT_SCHEDULE:设置定时的时间配置, 可以精确到秒,分钟,小时,天,周等。

(3)创建应用实例

在主工程目录添加 celery.py, 添加自动检索 django 工程tasks任务

vim artproject/celery.py

#!/usr/bin/env python  
# encoding: utf-8  
#目的是拒绝隐式引入,celery.py和celery冲突。
from __future__ import absolute_import,unicode_literals 
import os
from celery import Celery
from django.conf import settings

# 设置环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "artproject.settings")

#创建celery应用
app = Celery('art_project')
app.config_from_object('django.conf:settings')

#如果在工程的应用中创建了tasks.py模块,那么Celery应用就会自动去检索创建的任务。比如你添加了一个任#务,在django中会实时地检索出来。
app.autodiscover_tasks(lambda :settings.INSTALLED_APPS)

(4) 创建任务 tasks

每个任务本质上就是一个函数,在 tasks.py 中,写入你想要执行的函数即可。

在应用 art 中添加我们需要提供的异步服务和定时服务

vim art/tasks.py

#!/usr/bin/env python  
# encoding: utf-8  
from __future__ import absolute_import
import time
from django.core.mail import send_mail
from celery.utils.log import get_task_logger
from artproject.celery import app

from art.utils.send_mail import pack_html, send_email

@app.task
def tsend_email():
   url = "http://1000phone.com"
   receiver = 'diyuhuan@1000phone.com'
   content = pack_html(receiver, url)
   # content = 'this is email content.'
   send_email(receiver, content)
   print('send email ok!')

@app.task
def add(x, y):
   return x+y

上述我们把异步处理任务add和定时器任务 tsend_email 都放在了 tasks.py 中

(5)迁移生成 celery 需要的数据表

python manage.py migrate

此时数据库表结构多出了几个


celery_taskmeta            |
| celery_tasksetmeta         |
| djcelery_crontabschedule   |
| djcelery_intervalschedule  |
| djcelery_periodictask      |
| djcelery_periodictasks     |
| djcelery_taskstate         |
| djcelery_workerstate 

3、启动服务,测试

我们可以采用 python manage.py help 发现多出了 celery 相关选项。

(1)启动 django celery 服务

启动服务:

python manage.py celery worker --loglevel=info

此时异步处理和定时处理服务都已经启动了

(2)web端接口触发异步任务处理

我们在web端加入一个入口,触发异步任务处理add函数

在应用art的urls.py 中加入如下对应关系

from art.views import add_handler

url(r'^add', add_handler),

art/views.py 中加入处理逻辑

def add_handler(request):
   x = request.GET.get('x', '1')
   y = request.GET.get('y', '1')
   from .tasks import add
   add.delay(int(x), int(y))
   res = {'code':200, 'message':'ok', 'data':[{'x':x, 'y':y}]}
   return HttpResponse(json.dumps(res))

启动 web 服务,通过 url 传入的参数,通过 handler 的 add.delay(x, y) 计算并存入 mysql

http://127.0.0.1:8000/art/add?x=188&y=22

(4) 测试定时器,发送邮件

在终端输入 python manage.py celerybeat -l info

会自动触发每隔 30s 执行一次 tsend_email 定时器函数,发送邮件:

CELERYBEAT_SCHEDULE = {    #定时器策略
    #定时任务一: 每隔30s运行一次
    u'测试定时器1': {
        "task": "art.tasks.tsend_email",
        #"schedule": crontab(minute='*/2'),  # or 'schedule': timedelta(seconds=3),
        "schedule":timedelta(seconds=30),
        "args": (),
    },
}

具体发送邮件服务程序见下面的第4节

4、邮件发送服务

项目中经常会有定时发送邮件的情形,比如发送数据报告,发送异常服务报告等。

可以编辑文件 art/utils/send_mail.py, 内容编辑如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
#written by diyuhuan
#发送邮件(wd_email_check123账号用于内部测试使用,不要用于其他用途)

import smtplib  
from email.mime.multipart import MIMEMultipart  
from email.mime.text import MIMEText  
from email.mime.image import MIMEImage 
from email.header import Header
import time

sender = 'wd_email_check123@163.com'  
subject = u'api开放平台邮箱验证'
smtpserver = 'smtp.163.com'
username = 'wd_email_check123'
password = 'wandacheck1234'
mail_postfix="163.com"

def send_email(receiver, content):
    try:
        me = username+"<"+username+"@"+mail_postfix+">"
        msg = MIMEText(content, 'html', 'utf-8')
        msg['Subject'] = subject
        msg['From'] = sender
        msg['To'] = receiver
        smtp = smtplib.SMTP()  
        smtp.connect(smtpserver)  
        smtp.login(username, password)
        smtp.sendmail(sender, receiver, msg.as_string())  
        smtp.quit()
        return True
    except Exception as e:
        print('send_email has error with : ' + str(e))
        return False

def pack_html(receiver, url):
    html_content = u"<html><div>尊敬的用户<font color='#0066FF'>%s</font> 您好!</div><br>" \
                   "<div>感谢您关注我们的平台 ,我们将为您提供最贴心的服务,祝您购物愉快。</div><br>" \
                   "<div>点击以下链接,即可完成邮箱安全验证:</div><br>"  \
                   "<div><a href='%s'>%s</a></div><br>"  \
                   "<div>为保障您的帐号安全,请在24小时内点击该链接; </div><br>" \
                   "<div>若您没有申请过验证邮箱 ,请您忽略此邮件,由此给您带来的不便请谅解。</div>" \
                   "</html>" % (receiver, url, url)
    html_content = html_content
    return html_content

if __name__ == "__main__":
    url = "http://1000phone.com"
    receiver = 'diyuhuan@1000phone.com'
    #content = pack_html(receiver, url)
    content = 'this is email content. at %s.'%int(time.time())
    send_email(receiver,  content)

至此,在 celery ui 界面可以看到两类,定时器处理和异步处理。

5、启动 flower 服务

python manager celery flower

案例

Django 进阶之 celery
读书网站实现抢读功能

qd(request, id) :抢读视图函数

quereyQD(request,id) :查询抢读的视图函数

settings.py


INSTALLED_APPS = [
   'djcelery',
]

...
import djcelery

# 装载djcelery对象
djcelery.setup_loader()
# 配置消息中间件的位置
BROKER_URL = 'redis://127.0.0.1:6379/12'

CELERY_TIMEZONE = 'Asia/Shanghai'
# 配置批量调试器
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'

在主工程目录添加 celery.py, 添加自动检索django工程tasks任务

celery.py

from __future__ import absolute_import
import os
from celery import Celery

# 设置环境变量
from HArtPro import settings

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

# 创建Celery对象
app = Celery('hart')

# 加载配置
app.config_from_object('django.conf:settings')

# 自动发现task的异步任务
app.autodiscover_tasks(lambda :settings.INSTALLED_APPS)

当前 app 目录下建立 tasks.py

from MArtPro.celery import app
from utils import redis_cache

@app.task
def advanceArt(artId, userId):
    # 抢读文章(artId 文章id, userId 当前用户登录的Id)
    print('用户', userId, '正在抢读', artId)

    # 判断当前抢读的hash对象AdvanceArt长度是否达到5个
    if redis_cache.hlen('AdvanceArt') >= 5:
        return artId + '抢读失败'

    redis_cache.hset('AdvanceArt', userId, artId)

    return artId + '抢读成功!'

views.py

from redis_ import rd  # rd 对象
from art import tasks

def (request, artId):
    # 抢读
    login_user = request.session.get('login_user')

    if not login_user:
        return JsonResponse({'status': 101,
                             'msg': '亲,请先登录,再抢读,谢谢!'})

    # 判断当前用户是否已抢过
    user_id =json.loads(login_user).get('id')
    if redis_cache.hexists('AdvanceArt', user_id):
        return JsonResponse({'status': 205,
                             'msg': '亲,你只能抢一本'})
    # 任务延迟执行
    tasks.advanceArt.delay(artId, user_id)
    return JsonResponse({'status': 201,
                         'msg': '正在抢读...'})

def queryAdvance(request, artId):
    # 查询抢读是否成功
    login_user = request.session.get('login_user')

    if not login_user:
        return JsonResponse({'status': 101,
                             'msg': '亲,请先登录,再查看抢读,谢谢!'})

    user_id = json.loads(login_user).get('id')

    artId = redis_cache.hget('AdvanceArt', user_id)
    if artId:
        art = Art.objects.get(id=artId.decode())
        return JsonResponse({'status': 200,
                             'msg': '恭喜您,抢读%s 成功'%art.title})
    else:
        if redis_cache.hlen('AdvanceArt')< 5:
            return JsonResponse({'status': 202,
                                 'msg': '正在抢读...'})
        else:
            return JsonResponse({'status': 203,
                                 'msg': '抢读失败, 请下次碰碰运气!'})

前端通过定时器,每秒执行查询函数

作者:rottengeek
转载|原文链接:https://segmentfault.com/u/rottengeek

(如有侵权请联系删除)

转载于:https://blog.51cto.com/51reboot/2164835

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值