环境,win10。项目用的数据库需提前配置好,我用的mysql。redis数据库需提前配置好,并启动,我已在服务器中配置,并将连接信息存入win10环境变量,后面的代码里从环境变量中读取连接信息。
INSTALLED_APPS = [
…… # Django自带的默认配置已省略
'myApp', # Django应用名
'celery',
'django_celery_results' # 基于Django ORM实现了结果存储后端
]
# djcelery.setup_loader() # 需要安装django-celery 3.2.1或3.3.1,再import djcelery,在INSTALLED_APPS中添加djcelery。不写这句也行,不写的话不用安装django-celery。装的话可能在某个版本与celery扩展库或celery-with-redis扩展库冲突
BROKER_URL='redis://:'+os.getenv('CACHE_REDIS_PASSWORD')+'@'+os.environ.get('CACHE_REDIS_HOST')+':6379/1' # 任务队列,配置redis,/1是使用第1个数据库,总共是0-15。注意//后还有一个冒号
CELERY_RESULT_BACKEND='django-db' # 返回结果的存储地址,django-db是使用Django ORM对结果进行后端存储,若Django配置了mysql,则将任务信息和执行结果存入mysql中django_celery_results_taskresult表
CELERY_TASK_SERIALIZER='json' # 任务序列化和反序列化数据格式
CELERY_RESULT_SERIALIZER='json' # 结果序列化数据格式
# CELERY_ACCEPT_CONTENT=['json'] # 分布式接收数据的格式
# CELERY_TIMEZONE='Asia/Shanghai' # celery时区,已在celery.py中配置,这里不用再配置
# CELERY_ENABLE_UTC=True # 默认使用UTC时间
CELERY_TASK_RESULT_EXPIRES=60*60*24 # 后端存储的任务超过一天时,自动删除数据库中的任务数据,单位秒
CELERYD_MAX_TASKS_PER_CHILD=1000 # 每个worker执行1000次任务后,自动重启worker,防止任务占用太多内存导致内存泄漏
python manage.py migrate,迁移后数据库中多出一张表,django_celery_results_taskresult,里面是每个celery任务的信息和执行结果
在settings.py的同级目录内新建celery.py
from __future__ import absolute_import # 从绝对路径导入,只能写在第一行
import os
from celery import Celery
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', '14-celery.settings') # 从当前Django工程的配置中读取配置并设置,.settings前是当前Django工程名
app = Celery('portal') # 创建celery对象,第一个参数是应用名,必须传
app.conf.timezone='Asia/Shanghai' # 配置celery时区,默认是UTC
app.config_from_object('django.conf:settings') # 指定celery的配置来源,从当前项目的配置文件获取,即上面设置的
# app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
app.autodiscover_tasks() # 让celery工人(worker)自动从已注册的所有Django应用中发现任务(task),需在应用目录内新建tasks.py,不能用其他文件名
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
在settings.py的同级目录中的__init__.py添加
from __future__ import absolute_import # 从绝对路径导入,只能写在第一行
import pymysql # 若用的不是mysql,不用写这两行
pymysql.install_as_MySQLdb()
from .celery import app as celery_app # 从当前目录内的celery.py中导入app,并起别名celery_app,防止与其他app重名
在需要使用异步任务的Django应用目录内新建tasks.py,不能用其他文件名
import time
from celery import shared_task
@shared_task # 装饰后被装饰函数成为celery任务
def waitIO():
print('开始耗时操作') # 如网络状态差时,发邮件很慢
time.sleep(10)
print('结束耗时操作')
python manage.py migrate django_celery_results,前面迁移时已生成,这步可跳过
注意:启动celery和项目后,若又修改了代码,则需重启celery和项目,修改才生效。
非windows平台:celery -A 工程名 worker -l info
windows平台:celery4开始不能直接在windows上运行,直接运行上面的代码并触发任务后会报错ValueError: not enough values to unpack (expected 3, got 0),网上的说法是需要装gevent或eventlet,我都试了。当然也可以装4之前的版本,但由于我的Django版本高,用celery 3.1.25时,启动项目会报错,因为扩展的源码里用的render_to_response(),新版Django中用render()取代了它,所以要改源码,同时因render()的第一个参数是request,所以也要传一个request,我就不改源码了,换用4.4.0。
用gevent
启动项目,通过访问路由,正常执行task,celery控制台正常输出打印
用eventlet(测试结果说明不装也可以)
启动项目,通过访问路由,执行task后报错,即在输出打印后报错django.db.utils.DatabaseError: DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 2156058693704 and this is thread id 2153981599064.
解决方法(以下两种都无需安装eventlet或gevent),[]内是可选的
用--pool=solo启动后的截图
from django.urls import path
from myApp import views
urlpatterns=[
path('register/',views.register)
]
from django.shortcuts import render
from myApp.tasks import waitIO
from django.http import HttpResponse
# Create your views here.
def register(request):
if request.method=='GET':
return render(request,'myApp/register.html')
else:
username=request.POST.get('username')
password=request.POST.get('password')
waitIO.delay() # 将celery任务(task)放入队列(queue),让工人(worker)执行,若任务有参数,在delay()内传,这里传的话即给waitIO()传参。delay()本质上是调用apply_async()
return HttpResponse('注册成功')
注册
python manage.py runserver后,访问路由并提交表单,celery的控制台输出打印,且中间间隔了time.sleep()的10秒,数据库中django_celery_results_taskresult表中也存了本次任务的信息和执行结果。
由于celery任务是异步执行,所以不知道它什么时候执行完,主程序也就不能立刻获得celery任务执行结果。但一些装饰器可以解决这个问题,官方文档,被装饰的函数(依然写在tasks.py中)是回调函数,这个回调函数与celery任务绑定,根据需要,根据信号量在不同的时机执行回调函数。
以task_success为例,代码和详细介绍在下一篇文章末尾的celery发送邮件。
本文用到的参考链接
Django项目使用Celery - 知乎 (zhihu.com)
celery踩坑记(==4.3.0) - 知乎 (zhihu.com)
Django中使用Celery执行异步和定时任务的注意事项_大江狗-CSDN博客
在windows上 使用celery 报错_落叶无痕的博客-CSDN博客
Python Celery库不支持windows,报错 No module named ‘xxx’_HDmonster_root的博客-CSDN博客
关于Celery4.x以上在Win10下报错解决办法_请针对我谢谢的博客-CSDN博客
celery错误:DatabaseWrapper objects created in a thread can only - SegmentFault 思否
window下celery正常启动后能收到任务但不执行任务的解决办法 - 乔小生1221 - 博客园 (cnblogs.com)
celery——使用 - Ayca - 博客园 (cnblogs.com)
Python 之 Celery + Redis + Django 教程详细_YUAYU博客-CSDN博客
前言 - Celery 中文手册 (celerycn.io)
GitHub - open-source-translation/celery-cn: Celery中文手册
本文未用到的参考链接
实战教程!Django Celery 异步与定时任务 - 知乎 (zhihu.com)
Django中使用Celery的方法步骤_python_脚本之家 (jb51.net)
Django+Celery学习笔记5——定时推送消息 - 全栈测试开发日记 - 博客园 (cnblogs.com)
Django+Celery学习笔记4——django+celery+redis周期任务Crontabs设置 - 全栈测试开发日记 - 博客园 (cnblogs.com)