原文作者:我辈李想
版权声明:文章原创,转载时请务必加上原文超链接、作者信息和本声明。
前言
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 任务执行状态和日志。
一、安装 Celery
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple celery==5.3.1
Celery 的4大重点,task、Broker、worker、bacend
二、消息队列(Broker)
celery支持的消息队列常用的是RabbitMQ、Redis 等。这里就以Redis为例进行说明。
三、Django配置celery
1.创建celery_config.py文件
在django的settings.py同级目录添加celery_config.py,纤细配置参考celery官网配置
# -*- 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'}}
# 定时任务
CELERYBEAT_SCHEDULE = {
'task1': {
'task': 'upload-task', # 指定任务名称
'schedule': timedelta(seconds=5), # 任务执行时间,每5秒执行一次
'options': {
'queue': 'beat_tasks' # 指定队列
}
}
}
celery的其他参数请自行查阅celery官网
2.创建mycelery.py文件
# -*- 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()
3.修改__init__.py文件
from .mycelery import app as celery_app
__all__ = ('celery_app',)
4.settings文件添加配置
# 配置 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'
4.编写自己的task任务
django项目都是有很多app的(通过startapp命令创建),我们需要在app文件下新建tasks.py文件
from celery import shared_task
@shared_task
def send_email(to, subject, message):
# 发送邮件代码
5.celery调用
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 提供的结果存储和状态监控功能。
6、celery重试
我们在使用celery时,可能因为各种原因存在问题,导致程序没有按照我们制定的方式运行,对于有些任务,我们就需要继续执行。Celery提供了一些方法来处理任务失败和重试,可以在任务失败时自动重试或手动重试。
方式一:shared_task中重试
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 参数之后, 函数的参数发生变化, 多出了参数 self, 这这相当于把 div 编程了一个已绑定的方法, 通过 self 可以获得任务的上下文.。在定义任务时,可以设置一些参数来实现自动重试。例如:
@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秒钟。如果任务执行时出现异常,会自动引发重试任务。
方式三:Task继承
这种方式没怎么用过,需要的可以好好研究。
import celery
import time
from django.core.mail import send_mail
from celery.utils.log import get_task_logger
from wedo import app
logger = get_task_logger(__name__)
class TaskMonitor(celery.Task):
def on_failure(self, exc, task_id, args, kwargs, einfo):
"""failed callback"""
info=f'任务失败-- task id:{task_id} , arg:{args} , failed ! erros: {exc}'
logger.info(info)
send_mail('celery任务监控异常', info, 'sendmail@qq.com', ['tomail@qq.com'])
def on_success(self, retval, task_id, args, kwargs):
"""success callback"""
logger.info('task id:{} , arg:{} , successful !'.format(task_id,args))
def on_retry(self, exc, task_id, args, kwargs, einfo):
"""retry callback"""
logger.info('task id:{} , arg:{} , retry ! einfo: {}'.format(task_id, args, exc))
# @shared_task(base=TaskMonitor, bind=True)
@app.task(base=TaskMonitor, bind=True, name='post_file')
def post_file(self, file_names):
logger.info(self.request.__dict__)
try:
# 执行任务
except Exception as exc:
raise self.retry(exc=exc, countdown=60, max_retries=10)
这个自定义任务类可以手动引发重试,设置最大重试次数和计数器。
需要注意的是,自动重试和手动重试都可能导致无限循环重试的问题。在处理任务失败和重试时,一定要谨慎处理。
7.日志
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
8.队列(未证实)
# apply_async+设置
add.apply_async((1, 2), queue='lopri', countdown=60, expires=120,ignore_result=True) # 使用lopri队列,距现在60秒后开始执行,两分钟后过期,忽略结果
四、celery启动和停止(Worker)
1.windows启动celery
在命令行中进入Python脚本所在的目录,然后执行以下命令启动Celery worker:
celery -A tasks worker --loglevel=info
这里的-A
参数表示要加载的Celery应用模块,worker
表示启动worker进程,--loglevel
表示日志级别。
2.linux启动celery
3.停止
celery multi stop w1 -A proj -l info
# linux
ps auxww | grep 'celery worker' | awk '{print $2}' | xargs kill -9
五、celery监控
1.Flower监控
-
安装
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flower
-
启动
celery -A 项目名 flower --address=127.0.0.1 --port=5555