1.Clelery
1.1 什么是Clelery
- Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统专注于实时处理的异步任务队列同时也支持任务调度;(分布式,高可用)
- Celery 通过消息机制进行通信,通常使用中间人(Broker)作为客户端和职程(Worker)调节。启动一个任务,客户端向消息队列发送一条消息,然后中间人(Broker)将消息传递给一个职程(Worker),最后由职程(Worker)进行执行中间人(Broker)分配的任务。
1.2 Celery架构
- Celery 的架构由三部分组成,消息中间件(message broker),任务执行单元(worker)和任务执行结果存储(task result store)组成。
1.3 消息中间件
- Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
1.4 任务执行单元
- Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
1.5 任务结果存储
- Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
1.6 使用场景
- 异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等。
- 定时任务:定时执行某件事情,比如每天数据统计。
1.7 Celery的安装配置
pip install celery# 消息中间件:RabbitMQ/Redisapp=Celery('任务名',backend='xxx',broker='xxx')-
1.8 工作原理
- 结合生产消费者模型使用。
1.9 组件介绍
- Producer:调用了Celery提供的API、函数或者装饰器而产生任务并交给任务队列处理的都是任务生产者。
- Celery Beat:任务调度器,Beat进程会读取配置文件的内容,周期性地将配置中到期需要执行的任务发送给任务队列。
- Broker:消息代理,又称消息中间件,接受任务生产者发送过来的任务消息,存进队列再按序分发给任务消费方(通常是消息队列或者数据库)。Celery目前支持RabbitMQ、Redis、MongoDB、Beanstalk、SQLAlchemy、Zookeeper等作为消息代理,但适用于生产环境的只有RabbitMQ和Redis, 官方推荐 RabbitMQ。
- Celery Worker:执行任务的消费者,通常会在多台服务器运行多个消费者来提高执行效率。
- Result Backend:任务处理完后保存状态信息和结果,以供查询。Celery默认已支持Redis、RabbitMQ、MongoDB、Django ORM、SQLAlchemy等方式
1.10 序列化和反序列化
2.简单使用
2.1 异步任务
1.celery_task.py
import celeryimport timebackend='redis://:password@127.0.0.1:6379/1'broker='redis://:password@127.0.0.1:6379/2'cel=celery.Celery('test',backend=backend,broker=broker)@cel.taskdef send_email(name): print("向%s发送邮件..."%name) time.sleep(5) print("向%s发送邮件完成"%name) return "ok"@cel.taskdef send_sms(name): print("向%s发送短信..."%name) time.sleep(5) print("向%s发送短信完成"%name) return "ok"
2.produce_task.py
from celery_task import send_emailresult = send_email.delay("yuan")print(result.id)result2 = send_email.delay("alex")print(result2.id)
3.result.py结果
from celery.result import AsyncResultfrom celery_task import celasync_result=AsyncResult(id="8e16f7e9-c59f-453d-8aba-b97cb7f8d06e", app=cel)if async_result.successful(): result = async_result.get() print(result) # result.forget() # 将结果删除elif async_result.failed(): print('执行失败')elif async_result.status == 'PENDING': print('任务等待中被执行')elif async_result.status == 'RETRY': print('任务异常后正在重试')elif async_result.status == 'STARTED': print('任务已经开始被执行')
4.celery 启动
celery -A celery_task worker -l info -P eventlet
说明:如果不存在eventlet
,先手动安装pip install eventlet
2.2 定时任务
修改produce_task.py中的代码。
from celery_task import send_emailfrom datetime import datetime# 方式一# v1 = datetime(2020, 3, 11, 16, 19, 00)# print(v 云南党性培训 www.qduganxun.com 1)# v2 = datetime.utcfromtimestamp(v1.timestamp())# print(v2)# result = send_email.apply_async(args=["egon",], eta=v2)# print(result.id)# 方式二ctime = datetime.now()# 默认用utc时间utc_ctime = datetime.utcfromtimestamp(ctime.timestamp())from datetime import timedeltatime_delay = timedelta(seconds=30) # 设置当前时间为30秒后执行task_time = utc_ctime + time_delay# 使用apply_async并设定时间result = send_email.apply_async(args=["egon"], eta=task_time)print(result.id)
3.django中使用celery
3.1 调通django视图
from django.shortcuts import render,HttpResponse# Create your views here.def index(request): # 可放置异步功能模块 return HttpResponse("...")
3.2 创建目录结构
- 在项目的目录下(与
manage.py
同级的目录)创建目录;
mycelery/├── config.py├── __init__.py├── main.py└── sms/ # 一个功能呢过模块 ├── __init__.py ├── tasks.py # 必须交tasks
3.3 编写任务函数
# -*- coding: utf-8 -*-'''@Time : 2022/3/14 14:45@Author : ziqingbaojian@File : tasks.py'''# celery的任务必须写在tasks.py的文件中,别的文件名称不识别!!!from mycelery.main import appimport timeimport logginglog = logging.getLogger("django")@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名def send_sms(mobile): """发送短信""" print("向手机号%s发送短信成功!"%mobile) time.sleep(15) return "send_sms OK"@app.task # name表示设置任务的名称,如果不填写,则默认使用函数名做为任务名def send_sms2(mobile): print("向手机号%s发送短信成功!" % mobile) time.sleep(5) return "send_sms2 OK"
views.py
from django.shortcuts import render,HttpResponsefrom mycelery.sms.tasks import send_sms,send_sms2# Create your views here.def index(request): # 异步任务 # 1. 声明一个和celery一模一样的任务函数,但是我们可以导包来解决 send_sms.delay("110") send_sms2.delay("119") # send_sms.delay() # 如果调用的任务函数没有参数,则不需要填写任何内容'''按照逻辑应该停留在此处15分钟。''' return HttpResponse("...")
main.py
# 主程序import osfrom celery import Celery# 创建celery实例对象app = Celery("sms")# 把celery和django进行组合,识别和加载django的配置文件os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celeryPros.settings.dev')# 通过app对象加载配置文件app.config_from_object("mycelery.config")# 加载任务# 参数必须必须是一个列表,里面的每一个任务都是任务的路径名称# app.autodiscover_tasks(["任务1","任务2"])app.autodiscover_tasks(["mycelery.sms",])# 可以直接把相对应的路径导入# 启动Celery的命令# 强烈建议切换目录到mycelery根目录下启动# celery -A mycelery.main worker --loglevel=info
config.py
broker_url = 'redis://:password@127.0.0.1:6379/3'result_backend = 'redis://:password@127.0.0.1:6379/4'
异步完成,结果直接返回。
3.4 另一种方式
1.在settings.py
同级目录中,创建celery.py
;
import osfrom celery import Celeryfrom django.conf import settings# 设置celery的环境变量和django-celery的工作目录os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangocelery.settings")# 实例化celery应用,传入服务器名称app = Celery("djangocelery")# 加载celery配置app.config_from_object("django.conf:settings")# 如果在项目中,创建了task.py,那么celery就会沿着app去查找task.py来生成任务app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
2.添加配置文件
ROKER_URL = 'redis://:password@127.0.0.1:6379/14' # 任务容器地址,redis数据库地址CELERY_RESULT_BACKEND = 'redis://:password@127.0.0.1:6379/15' # 任务结束的地址
3.在settings.py
同级目录中的__init__.py
中添加
from .celery import app as celery_app__all__ = ['celery_app']
4.在app下新建tasks.py
from __future__ import absolute_import, unicode_literalsimport timefrom celery import shared_task@shared_taskdef adds(): for i in range(0, 10): print(i) time.sleep(1) return 'finish'
5.编写views
和url
from django.shortcuts import render,HttpResponsefrom django.http.response import JsonResponse# Create your views here.from app01 import tasksfrom celery.result import AsyncResultdef index(request): """ 进入这个url的时候就触发异步任务,并在session中记录task_id """ res = tasks.adds.delay() request.session['task_id'] = res.task_id return JsonResponse({'status': 'successful', 'task_id': res.task_id})def dasd(request): """ 进入url就会去获取session中的task_id,并检测任务. 若任务还在进行就显示页面还在加载,若进行完成就显示hahaha """ task_id = request.session.get('task_id') if task_id and AsyncResult(task_id).state == 'PENDING': # 加载时的状态为PENDING return HttpResponse('页面正在加载...') return HttpResponse('hahaha')
urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^index/', views.index), re_path(r'^dasd/', views.dasd),]
继续努力,终成大器!