Django配置Celery执行异步和定时任务
Celery简介
Celery是一个基于Python开发的简单、灵活且可靠的处理大量消息的分布式系统,并且提供维护的一个工具。支持使用任务队列的方式在分布式机器的进程、线程上执行任务调度。采用生产者-消费者模型。
-
消息队列
任务队列是一种在线程或分布式机器间分布任务的机制。消息队列的输出是工作的一个单元,称为任务,Worker进程持续监视队列中是否有新的需要处理的新任务。Celery使用消息通信,通常使用中间人(Broker)在客户端和Worker之间联系,通过客户端向队列中添加消息后broker把消息发送给Worker。
-
使用场景
- 异步任务:将耗时的操作任务交给Celery去异步执行,比如发送短信、邮件、消息推送、音视频处理等
- 定时任务:类似于crontab
-
Celery组件
- Celery Beat:任务调度器
Beat进程会读取配置文件中的内容,按照定时任务的设置,定期将需要执行的任务发送给任务队列 - Celery Worker:消费者
当任务队列中任务的时候,中间人会通知Worker去处理任务,通常一台服务器上运行多个消费者,提高运行效率 - Broker:中间人,消息代理
接受生产者发送的任务,将队列中需要处理的任务消息发送给消费者(消息队列) - Producer:任务生产者
调用Celery API,函数或者装饰器,产生任务并交给任务队列处理的都是任务生产者 - Backend:任务处理完成之后的保存状态的信息和结果,以供查询
- Celery Beat:任务调度器
-
消息代理
生产环境的消息代理有RabbitMQ和Redis,RabbitMQ更好。
Windows上使用Celery
-
下载依赖包
# 安装Celery pip install Celery pip install redis
-
项目中的目录结构(简化)
u17/ |-- comic | |-- admin.py | |-- apps.py | |-- __init__.py | |-- models.py | |-- tasks.py | |-- tests.py | |-- urls.py | `-- views.py |-- manage.py |-- README `-- u17 |-- celery.py |-- __init__.py |-- settings.py |-- urls.py `-- wsgi.py
-
配置
u7/celery.py
文件from __future__ import absolute_import, unicode_literals import os from celery import Celery, platforms from datetime import timedelta from django.conf import settings os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'u17_celery.settings') # 使用Redis做MQ app = Celery('u17_celery', broker='redis://:123456@127.0.0.1:6379/0') app.config_from_object('django.conf:settings') # 指定自动检索异步任务的应用 app.autodiscover_tasks(app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)) # 允许root 用户运行celery platforms.C_FORCE_ROOT = True @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
-
u17/__init.py
增加配置,确保Django项目启动时能够加载appfrom __future__ import absolute_import from .celery import app as celery_app __all__ = ['celery_app'] import pymysql pymysql.install_as_MySQLdb()
-
为需要执行异步任务的应用创建tasks.py文件,例如
u17/comic/tasks.py
注意tasks.py必须建在各app的根目录下,且只能叫tasks.py,不能随意命名
from __future__ import absolute_import from celery import shared_task from comic.models import * from faker import Faker @shared_task def add_comics(): faker = Faker() for i in range(100): comic = Comic() comic.name = faker.name() comic.save()
创建一个简单的model
from django.db import models class Comic(models.Model): comic_id = models.CharField(max_length=32) name = models.CharField(max_length=128) cover = models.CharField(max_length=1024) category = models.CharField(max_length=128) class Meta: db_table = 'comic'
-
在
u17/comic/views.py
引入异步任务from django.http import JsonResponse from comic.tasks import add_comics def insert_comics(request): add_comics.delay() return JsonResponse({'message': 'success'})
- 使用函数名.delay()即可使函数异步执行
- 可以通过
result.ready()
来判断任务是否完成处理 - 如果任务抛出一个异常,使用
result.get(timeout=1)
可以重新抛出异常 - 如果任务抛出一个异常,使用
result.traceback
可以获取原始的回溯信息
-
启动
# 先启动自己的django项目 python manage.py runserver # 启动celery celery -A u17 worker -l info # 使用Windows 10做开发且使用celery 4.x版本需要先安装一个三方库作为辅助 pip install eventlet # 然后启动celery的消费者的时候需要多加一个参数 celery -A common.utils worker -l info -P eventlet
定时任务
- 在
u7/celery.py
文件添加配置支持定时任务from celery.schedules import crontab app.conf.update( CELERYBEAT_SCHEDULE = { 'add_comics': { 'task': 'comic.tasks.add_comics', 'schedule': timedelta(seconds=20), } } )
Centos上使用Celery
-
安装RabbitMQ
# 安装 yum install rabbitmq-server # 启动 rabbitmq-server -detached # 设置RabbitMQ rabbitmqctl add_user wangdachui 123456 rabbitmqctl add_vhost wdchost rabbitmqctl set_user_tags wangdachui mytag rabbitmqctl set_permissions -p wdchost ".*" ".*" ".*" rabbitmqctl list_queues -p wdchost
-
安装Celery
pip install celery -i https://pypi.douban.com/simple
-
配置
u7/celery.py
文件from __future__ import absolute_import, unicode_literals import os from celery import Celery, platforms from datetime import timedelta from django.conf import settings os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'u17.settings') # 使用RabbitMQ做MQ app = Celery('u17', backend='amqp', broker='amqp://wangdachui:123456@127.0.0.1:5672/wdchost') app.config_from_object('django.conf:settings') # 指定自动检索异步任务的应用 app.autodiscover_tasks(app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)) # 允许root用户运行celery platforms.C_FORCE_ROOT = True @app.task(bind=True) def debug_task(self): print('Request: {0!r}'.format(self.request))
后续操作和上一样…