原文作者:我辈李想
版权声明:文章原创,转载时请务必加上原文超链接、作者信息和本声明。
Django 是一个基于 Python 的 Web 开发框架,而 Celery 是一个 Python 实现的异步任务队列(Task Queue),它能够处理大规模的分布式任务,用于实现 Django 中的异步任务。
使用 Celery 和 Django 可以实现以下功能:
异步任务处理:Celery 可以将耗时的任务异步处理,加速 Web 应用的响应速度。
定时任务:Celery 可以通过设置定时任务,定期执行某些任务,例如定时清理数据库。
异常处理:Celery 可以在任务执行失败时捕获异常,并进行相应的处理,例如发送邮件通知管理员。
分布式任务:Celery 可以将任务分发到多个节点进行处理,提高任务执行效率和并发性。
在 Django 中使用 Celery,需要进行以下几个步骤:
安装 Celery 和消息队列(例如 RabbitMQ、Redis 等)。
在 Django 中配置 Celery。
定义任务函数,并使用 Celery 的装饰器进行装饰。
在 Django 视图函数中调用任务函数,并将任务加入 Celery 队列执行。
监控 Celery 任务执行状态和日志。
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple celery==5.3.1
celery支持的消息队列常用的是RabbitMQ、Redis 等。这里就以Redis为例进行说明。
Redis的安装和使用
在django的settings.py同级目录添加celery_config.py
# -*- coding: utf-8 -*-
"""
@author:lpf
@file: celery_config.py
@time: 2023/8/7 17:17
"""
# 启用 UTC 时区
import os
from datetime import timedelta
# 时区,与django的TIMEZONE一致
CELERY_TIMEZONE = "Asia/Shanghai"
CELERY_TASK_TRACK_STARTED = True
# 有些情况防止死锁
CELERYD_FORCE_EXECV = True
# 任务失败允许重试
CELERY_ACKS_LATE = True
# Worker并发数量,一般默认CPU核数,可以不设置
CELERY_WORKER_CONCURRENCY = 2 # CELERYD_CONCURRENCY = 4
# 每个worker最多执行的任务数,超过这个就将worker进行销毁,防止内存泄漏,默认无限
CELERYD_MAX_TASKS_PER_CHILD = 100
# 单个任务运行的最大时间,超过这个时间,task就会被kill
CELERY_TASK_TIME_LIMIT = 30 * 60
# 过期时间,默认一天
CELERY_RESULT_EXPIRES = 30 * 60
# 任务限流
# CELERY_TASK_ANNOTATIONS = {'tasks.add': {'rate_limit': '10/s'}}
# celery 内容等消息的格式设置,默认json
if os.name != "nt":
# Mac and Centos
# worker 启动命令:celery -A 项目名 worker -l info
CELERY_ACCEPT_CONTENT = ['application/json', ]
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
else:
# windows
# pip install eventlet
# worker 启动命令:celery -A 项目 worker -l info -P eventlet
CELERY_ACCEPT_CONTENT = ['pickle', ]
CELERY_TASK_SERIALIZER = 'pickle'
CELERY_RESULT_SERIALIZER = 'pickle'
# 定义任务模块的名称,需要在 Django 项目中创建 tasks.py 文件(与下一步的app配置设置一个即可)
# CELERY_IMPORTS = ('commodity.tasks',)
# 队列
CELERY_QUEUES = {
# 定时任务队列
'beat_tasks': {
'exchange': 'beat_tasks',
'exchange_type': 'direct',
'binding_key': 'beat_tasks'
},
# 普通任务队列
'work_tasks': {
'exchange': 'work_tasks',
'exchange_type': 'direct',
'binding_key': 'work_tasks'
}
}
# 设置默认的队列
CELERY_DEFAULT_QUEUE = 'work_tasks'
# 定时任务
CELERYBEAT_SCHEDULE = {
'task1': {
'task': 'upload-task', # 指定任务名称
'schedule': timedelta(seconds=5), # 任务执行时间,每5秒执行一次
'options': {
'queue': 'beat_tasks' # 指定队列
}
}
}
celery的其他参数请自行查阅celery官网
# -*- coding: utf-8 -*-
"""
@author:lpf
@file: celery.py
@time: 2023/7/27 17:22
"""
import os
from celery import Celery
# 设置django环境变量
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tianyiapi.settings') # 项目配置
# 创建celery实例化对象
app = Celery('tianyiapi') # 项目名
# 启动项目celery配置
app.config_from_object('django.conf:settings', namespace='CELERY')
# 自动发现项目中的tasks
app.autodiscover_tasks()
from .mycelery import app as celery_app
__all__ = ('celery_app',)
# 配置 Celery
from .mycelery import *
# 指定 Broker 使用 Redis,Broker 负责任务的分发和调度
CELERY_BROKER_URL = 'redis://127.0.0.1:6379/1'
# 指定结果存储 Backend 使用 Redis,Backend 负责存储任务执行结果
CELERY_RESULT_BACKEND = 'redis://localhost:6379/1'
# django-celery-results 设置
CELERY_RESULT_BACKEND = 'django-db'
django项目都是有很多app的(通过startapp命令创建),我们需要在app文件下新建tasks.py文件
from celery import shared_task
@shared_task
def send_email(to, subject, message):
# 发送邮件代码
celery调用主要有delay和apply_async两种方式,其中delay可设置参数较少,apply_async可以设置延时、过期等。
# delay方法+参数
task_name.delay(args1, args2, kwargs=value_1, kwargs2=value_2)
# apply_async+参数
task.apply_async(args=[arg1, arg2], kwargs={key:value, key:value})
# apply_async+设置
add.apply_async((1, 2), queue='lopri', countdown=60, expires=120,ignore_result=True) # 使用lopri队列,距现在60秒后开始执行,两分钟后过期,忽略结果
示例:
from django.shortcuts import render
from .tasks import send_email
def contact(request):
# 处理表单提交
# 将任务加入队列
send_email.delay(to, subject, message)
return render(request, 'contact.html', {'success': True})
4和5使用 @shared_task
装饰器定义了一个 send_email
异步任务,然后在 Django 视图函数中调用该任务,并使用 delay()
方法将任务加入 Celery 队列执行。
注意,在使用 Celery 进行异步任务处理时,需要注意任务的并发性和任务的执行结果。为了提高任务的并发性,可以将任务分发到多个节点进行执行;为了获取任务的执行结果,可以使用 Celery 提供的结果存储和状态监控功能。
我们在使用celery时,可能因为各种原因存在问题,导致程序没有按照我们制定的方式运行,对于有些任务,我们就需要继续执行。Celery提供了一些方法来处理任务失败和重试,可以在任务失败时自动重试或手动重试。
from celery import shared_task
@shared_task
def my_task(*args, **kwargs):
try:
# 执行任务
except Exception as exc:
# 引发重试
raise my_task.retry(exc=exc, countdown=60, max_retries=10)
在上述代码中,当任务执行发生异常时,会引发一个重试,参数说明如下:
exc:异常对象,用于调试错误;
countdown:重试之前的倒计时,以秒为单位;
max_retries:重试的最大次数。
以上例子中,每次重试之间的间隔为60秒,最多重试10次。
在定义任务时,可以设置一些参数来实现自动重试。例如:
@app.task(bind=True, max_retries=3, default_retry_delay=10)
def my_task(self, *args, **kwargs):
try:
# 执行任务
except Exception as exc:
raise self.retry(exc=exc)
这个任务被设置为最大重试3次,每次重试间隔10秒钟。如果任务执行时出现异常,会自动引发重试任务。
这种方式没怎么用过,需要的可以好好研究。
class MyTask(Task):
def run(self, *args, **kwargs):
try:
# 执行任务
except Exception as exc:
raise self.retry(exc=exc, countdown=60, max_retries=10)
my_task = MyTask.bind(app)
@app.task(base=MyTask,bind=True)
这个自定义任务类可以手动引发重试,设置最大重试次数和计数器。
需要注意的是,自动重试和手动重试都可能导致无限循环重试的问题。在处理任务失败和重试时,一定要谨慎处理。
from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)
@app.task
def add(x, y):
logger.info('Adding {0} + {1}'.format(x, y))
return x + y
在命令行中进入Python脚本所在的目录,然后执行以下命令启动Celery worker:
celery -A tasks worker --loglevel=info
这里的-A
参数表示要加载的Celery应用模块,worker
表示启动worker进程,--loglevel
表示日志级别。
celery multi stop w1 -A proj -l info
# linux
ps auxww | grep 'celery worker' | awk '{print $2}' | xargs kill -9
安装
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flower
启动
celery flower -A 项目名 --address=127.0.0.1 --port=5555
文档地址