Django + Celery + Redis最佳实践
1.Django安装
pip install django
2.Redis安装
-
下载redis,压缩包内可直接用(windows)
百度网盘链接:https://pan.baidu.com/s/12umBDyeNOW5Jr4WfayHPaQ
提取码:a86a -
windows需配置环境变量,方便启动redis服务
-
启动redis服务,linux可设置系统自启
3. Celery安装
-
默认最新,若有版本需要可指定版本 pip install celery
-
不用djcelery,使用celery自由度高,可扩展性强。
4. 配置Django项目
-
创建项目 django-admin startproject mysite (mysite为项目名)
-
新建应用,进到项目目录 python manage.py startapp myapp (myapp为应用名)
-
项目结构如下
tips: celery_tasks文件夹为任务配置,celerybeat这些是启动定时任务时生成的(不用管) -
从根urls到app的urls路由转发自行配置
5. Celery异步/定时任务配置
-
celery_tasks下主要两个配置文件main.py,config.py
-
任务逻辑函数放于 celery_tasks/base_info/tasks.py (一定以tasks命名)
-
main.py
import os from celery import Celery os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') # 创建celery应用 app = Celery( 'hello_celery', broker='redis://127.0.0.1:6379/14', # 使用14号库当作broker backend='redis://127.0.0.1:6379/15' # 使用15号库 存放结果 ) # 导入celery配置 app.config_from_object('celery_tasks.config') # 定时任务需要添加 app.autodiscover_tasks(['celery_tasks.base_info']) # 在出现worker接受到的message出现没有注册的错误时,使用下面一句能解决 # imports = ("tasks",)
tips:broker和backend充当消息中间件,相当于生产者、结果存放处。
-
config.py
from celery.schedules import crontab from kombu import Queue from celery_tasks.main import app from celery.schedules import timedelta # worker 的并发数,与内核数相关 worker_concurrency = 8 # 每个worker 每次去broker中预取任务数量 worker_prefetch_multiplier = 4 # 每个worker 执行多少任务死掉,默认无限次。设置防止内存溢出 worker_max_tasks_per_child = 100 # 任务结果保存时间,默认是一天。单位:秒 result_expires = 60 # 非常重要,有些情况下可以防止死锁, 配置的优先级 装饰器>config配置> 队列启动命令 # 队列启动命令 --soft-time-limit=10 # CELERYD_FORCE = True # 4.0版本后不支持,可用下方软硬时间限制替代 task_time_limit = 60 * 1 # 硬时间限制,超过此时间worker会被杀死并用新的worker执行 task_soft_time_limit = 10 # 软时间限制,作用是在硬限制发生前启用新的worker broker_transport_options = {'visibility_timeout': 60 * 60, "max_retries": 5} # 任务发出后,经过一段时间还未收到acknowledge , 就将任务重新交给其他worker执行 worker_disable_rate_limits = True # 读取任务结果一般性能要求不高,所以使用了可读性更好的JSON result_serializer = "json" task_queues = ( Queue("default", routing_key="default"), Queue("base", routing_key="base.#"), ) task_routes = { # 以上如果指定队列执行,则下列指定方式失效 "test": {'queue': 'base', 'routing_key': 'base.info'}, # "test1": {'queue': 'base', 'routing_key': 'base.test'}, } # 定义默认队列和默认的交换机routing_key task_default_queue = 'default' task_default_exchange = 'default' task_default_routing_key = 'default' # 设置时区,默认UTC timezone = 'Asia/Shanghai' # 配置定时任务 app.conf.beat_schedule = { "week_sms_task": { "task": "week_sms", "schedule": timedelta(seconds=10), }, "monday_task": { "task": "monday_task", "schedule": crontab(hour=8, day_of_week=1), } }
更详细参数配置参考: task配置文档
-
base_info/tasks.py
from celery_tasks.main import app
# 采用装饰器方式配置软时间限制
@app.task(name='sendsms', soft_time_limit=10)
def sendsms():
"""
执行发邮件业务
:return:
"""
return "send msg success"
@app.task(name='calculate')
def calculate(a, b):
total = a + b
return total
@app.task(name='week_sms')
def week_sms():
return "week_sms send!"
@app.task(name='monday_task')
def monday_task():
return "monday_task send!"
6. 业务Views引用异步任务
views.py
from django.shortcuts import render
from django.http import HttpResponse
from celery_tasks.base_info.tasks import calculate, sendsms
# Create your views here.
def sms(request):
print('this is sms!')
# sendsms.apply_async(queue='default', routing_key='default')
task = sendsms.apply_async(queue='base', routing_key='base.info') # 使用指定任务队列、路由。可错峰执行异步任务
print(task.state) # 任务状态
print(task.state)
print(task.id) # 任务id
print(task.get(timeout=1)) # 任务执行结果
print(task.state)
print(task.successful()) # 任务是否成功
return HttpResponse("sms ok!")
def calcu(request):
print('this is calculate!')
task = calculate.delay(1, 2)
print(task.failed())
return HttpResponse("calculate ok!")
更多task属性:task属性参考
7. 启动celery
-
启动前需执行数据迁移,python manage.py migrate (可不用执行 makemigrations)
-
在项目主目录manage.py同级启动命令: celery -A celery_tasks.main worker -l info
-
celery对于windows支持不怎么好,celery4以上版本在windows10执行 celery -A celery_task worker -l info 服务启动正常,但是调用delay后出现报错。
Task handler raised error: ValueError: not enough values to unpack (expected 3, got 0)
解决:
安装eventlet
启动命令:celery -A celery_tasks.main worker -l info -P eventlet
- 启动django项目:python manage.py runserver
8. 调用异步任务
-
访问url
-
查看redis服务,已经执行完任务收到结果
9. 启动定时任务
-
异步和定时任务需要单独的启动命令,启动后会自动执行定时任务
celery -A celery_tasks.main beat -l info
定时任务配置参考 config.py 中末尾配置