Celery分布式任务队列
Celery官方文档
Clery官方文档中文版
Celery简介
Celery是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列
Celery异步任务框架
Celery提供异步任务框架,主要有以下三大功能:
1. 执行异步任务
2. 执行延迟任务
3. 执行定时任务
1.可以不依赖任何服务器,通过自身命令,启动服务(内部支持socket)
2.celery服务为为其他项目服务提供异步解决任务需求的
注:会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求
注意:
在Windows平台上运行Celery可能会有问题:
Celery is a project with minimal funding,
so we don’t support Microsoft Windows.
Please don’t open any issues related to that platform.
Celery架构
Celery的架构由三部分组成:
1. 消息中间件(message broker)、
2. 任务执行单元(worker)
3. 任务执行结果存储(task result store)组成
消息中间件:broker(需要使用第三方,redis,rabbitmq)
Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ, Redis等等
任务执行单元:worker(celery提供的)
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
任务结果存储:backend(使用第三方)
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等
使用场景
异步执行:解决耗时任务,将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
延迟执行:解决延迟任务
定时执行:解决周期(周期)任务,比如每天数据统计
Celery的使用
安装
pip install celery
Celery的两种结构
1.基本结构 # 所有文件都放在一个py文件当中 2.包管理结构 # 推荐使用包管理,结构更清晰 # 建一个包 包名任意 包内必须有个celery.py文件 文件内书写Celery()产生对象的语句
方式一:基本结构
1.创建一个 celery_task.py 文件,实例化得到app,编写任务(写一个函数...),用装饰器装饰
# celery_task.py from celery import Celery # broker = 'redis://:123456@127.0.0.1:6379/1' # 123456是密码 # backend = 'redis://:123456@127.0.0.1:6379/2' # 123456是密码 broker = 'redis://127.0.0.1:6379/1' # broker任务队列 消息中间件 backend = 'redis://127.0.0.1:6379/2' # 结构存储,执行完的结果存在这 # 1.实例化得到一个对象app 指定broker消息中间件 和 backend结果仓库 app = Celery(__name__,broker=broker,backend=backend) # app对象的名字,把当前文件的名字给它 # 2.添加任务(就是add函数) 用装饰器@app.task装饰该任务 @app.task def add(x,y): print(x,y) return x+y
2.在其他系统 add_task.py 文件中,提交任务(导入任务)
# add_task.py from celery_task import add # add(3,4) # 只是直接执行,不会被添加到broker中 res = add.delay(4,5) # 向broker中添加一个任务 要用到delay print(res) # 得到一个id值: c9168245-8966-4cba-9412-1a57218b7daf
# 只要 add_task.py 一执行,就会向Broker提交一个任务,
# 当Worker启动时,任务执行的结果会被存储到Backend当中
3.启动 worker:需要在 Terminal中 cd 到 celery_task.py 所在的文件夹下
# 用命令来执行 非Windows系统: # 命令:celery -A 模块名 worker -l info celery -A celery_task worker -l info
Windows系统: pip3 install eventlet # Windows下需要借助eventlet # 命令:celery -A 模块名 worker -l info -P eventlet celery -A celery_task worker -l info -P eventlet
注意版本区别: # 3.x及以前: # celery worker -A 模块名 -l info -P eventlet celery worker -A celery_task -l info -P eventlet # 需要切换到指定文件夹 # 4.x及以后: # celery -A 模块名 worker -l info -P eventlet celery -A celery_task worker -l info -P eventlet
4.查看执行的结果,get_result.py
# get_result.py from celery_task import app from celery.result import AsyncResult id = 'eda5822a-6c3e-4f0f-b399-0802226bc3b0' if __name__ == '__main__': asy = AsyncResult(id=id, app=app) if asy.successful(): # 顺利执行完了,可以取结果了 res = asy.get() # 任务执行的结果 print(res) elif asy.failed(): print('任务失败') elif asy.status == 'PENDING': print('任务等待中被执行') elif asy.status == 'RETRY': print('任务异常后正在重试') elif asy.status == 'STARTED': print('任务已经开始被执行')
方式二:包管理(推荐,结构清晰)
1.创建一个包,包内必须有一个名为 celery.py 的文件,将Celery()产生对象的语句放在该文件中
包格式如下:
celery_task # 包名 __init__.py celery.py # 必须叫这个名字 task1.py # 一堆任务 task2.py # 一堆任务 任务提交-正常情况写在项目中.py 结果查看--正常情况写在项目中.py
celery_task/celery.py 文件中内容如下:
# celery.py from celery import Celery broker = 'redis://127.0.0.1:6379/1' # broker任务队列 消息中间件 backend = 'redis://127.0.0.1:6379/2' # 结构存储,执行完的结果存在这 # 1.实例化得到一个对象app 指定broker消息中间件 和 backend结果仓库 app = Celery(__name__,broker=broker,backend=backend, include=['celery_task.task1','celery_task.task2'])
2.书写任务,可以有很多
celery_task/task1.py文件中:
# task1.py from .celery import app # 导入app @app.task def send_code(phone,code): print('%s发送了验证码%s'%(phone,code)) return '号码:%s,验证码:%s'%(phone,code)
celery_task/task2.py文件中:
# task2.py from .celery import app # 导入app @app.task def upload(name,img): print('%s更新了照片%s'%(name,img)) return '%s更新了照片%s'%(name,img)
3.启动 worker 需要cd到 celery 包所在的路径(scripts路径下)
# 用命令来执行 非Windows系统: # 命令:celery -A 包名 worker -l info celery -A celery_task worker -l info Windows系统: pip3 install eventlet # Windows下需要借助eventlet # 命令:celery -A 包名 worker -l info -P eventlet celery -A celery_task worker -l info -P eventlet 注意版本区别: # 3.x及以前: # celery worker -A 包名 -l info -P eventlet celery worker -A celery_task -l info -P eventlet # 需要切换到指定文件夹 # 4.x及以后: # celery -A 包名 worker -l info -P eventlet celery -A celery_task worker -l info -P eventlet
4.提交任务:提交异步任务,提交延时任务
可以是项目中的任意位置的文件,只要导入文件没问题 直接右键执行
# submit_task.py from celery_task.task1 import send_code from celery_task.task2 import upload # 提交异步任务 res = send_code.delay('110','995') print(res) # 提交延迟任务 from datetime import datetime,timedelta # 需要获取utc时间,延迟10秒提交 eta = datetime.utcnow() + timedelta(seconds=10) res1 = upload.apply_async(args=('yessir','清明上河图'),eta=eta) print(res1)
5.通过id查看执行结果 get_result.py 直接右键执行
# get_result.py from celery_task.celery import app from celery.result import AsyncResult id = '34a48e05-9873-49a4-b190-1d2fbed875ef' # 根据id获取结果 if __name__ == '__main__': asy = AsyncResult(id=id, app=app) if asy.successful(): # 顺利执行完了,可以取结果了 res = asy.get() # 任务执行的结果 print(res) elif asy.failed(): print('任务失败') elif asy.status == 'PENDING': print('任务等待中被执行') elif asy.status == 'RETRY': print('任务异常后正在重试') elif asy.status == 'STARTED': print('任务已经开始被执行')
上面,我们通过 celery 包管理的方式,执行了异步任务和延迟任务,
其实,我们还可以通过 celery 执行定时任务,如下:
第一步: 在 celery.py 中加入配置
# celery.py from celery import Celery broker = 'redis://127.0.0.1:6379/1' # broker任务队列 消息中间件 backend = 'redis://127.0.0.1:6379/2' # 结构存储,执行完的结果存在这 # 1.实例化得到一个对象app 指定broker消息中间件 和 backend结果仓库 app = Celery(__name__,broker=broker,backend=backend, include=['celery_task.task1','celery_task.task2']) # 执行定时任务 # 时区 app.conf.timezone = 'Asia/Shanghai' # 是否使用UTC app.conf.enable_utc = False # 任务的定时配置 from datetime import timedelta from celery.schedules import crontab app.conf.beat_schedule = { 'send_code': { # send_code为key名 'task': 'celery_task.task1.send_code', # 指定task # 'schedule': timedelta(seconds=5), # 每隔5秒 'schedule': crontab(hour=8, day_of_week=1), # 每周一早八点 'args': ('110', 995), # 函数传的参数 } } # 执行定时任务其实就是celery.py中多了配置 以及多执行一个自动添加任务的beat服务
第二步:跟方式二一样,写很多任务
第三步:启动worker,启动beat(自动添加任务)
这两个都需要cd到 celery 包所在的路径(scripts路径下)
# 启动两个任务(worker 和 beat)
cd scripts # 需切换到指定路径 celery -A celery_task worker -l info -P eventlet celery -A celery_task beat -l info # 注意:这里也有版本的区别,详情见方式一/方式二的worker启动
此时,每隔5秒会提交一次任务(或者每周一早上八点会提交任务)
Celery框架在django项目中的使用
celery框架django项目工作流程 1)加载django配置环境 2)创建Celery框架对象app,配置broker和backend,得到的app就是worker 3)给worker对应的app添加可处理的任务函数,用include配置给worker的app 4)完成提供的任务的定时配置app.conf.beat_schedule 5)启动celery服务,运行worker,执行任务 6)启动beat服务,运行beat,添加任务 # 重点:由于采用了django的反射机制,使用celery.py所在的celery_task包必须放置项目的根目录下
1.将 celery_task 包放在 项目根路径下(大luffyapi文件夹下)
在 celery_task 包内的 celery.py 文件中配置如下:
# celery.py
from celery import Celery # 加载django环境 import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyapi.settings.dev") django.setup() broker='redis://127.0.0.1:6379/1' #broker任务队列 backend='redis://127.0.0.1:6379/2' # 结构存储,执行完的结果存在这 app=Celery(__name__,broker=broker,backend=backend,include=['celery_task.home_task',]) # 执行定时任务 # 时区 app.conf.timezone = 'Asia/Shanghai' # 是否使用UTC app.conf.enable_utc = False # 任务的定时配置 from datetime import timedelta from celery.schedules import crontab app.conf.beat_schedule = { 'add-task': { 'task': 'celery_task.home_task.banner_update', 'schedule': timedelta(seconds=60), } }
2.在Terminal终端中:cd到大luffyapi路径下:启动worker,启动beat
在Terminal终端中: cd到大luffyapi路径下:启动worker,启动beat worker:celery -A celery_task worker -l info -P eventlet beat:celery -A celery_task beat -l info
3.添加定时任务:home_task.py
# home_task.py from .celery import app # 导入app @app.task def banner_update(): from home import models from home import serializer from django.conf import settings from django.core.cache import cache banner_queryset = models.Banner.objects.filter( is_delete=False, is_show=True).order_by('display_order')[:settings.BANNER_DISPLAY] banner_serializer = serializer.BannerModelSerializer(instance=banner_queryset,many=True) # 通过for循环取值 然后拼接图片完整路径 加载到缓存中 for banner in banner_serializer.data: banner['img'] = 'http://127.0.0.1:8000' + banner['img'] # 将图片数据存入缓存中 cache.set('banner_list',banner_serializer.data) banner_list = cache.get('banner_list') print(banner_list) return True
此时,如果每隔1分钟,就会向提交一次定时任务,刷新轮播图的数据信息