Celery 官方文档中文版:http://docs.jinkan.org/docs/celery/
一、简介及安装
1.1、什么是celery?
celery是一个可以处理大量消息的分布式任务队列。
分布式决定了可以有多个 worker 的存在,队列表示其是异步操作,即存在一个产生任务提出需求的工头,和一群等着被分配工作的码农。
celery支持使用任务队列的方式在分布的机器、进程、线程上执行任务调度。
- 任务队列就是一种在线程或机器间分发任务的机制。
1.2、它能做什么?
- 专注于实时处理的异步任务队列:异步任务
- 支持任务调度 :定时任务
1.3、使用场景
- 异步任务:将耗时操作任务提交给Celery去异步执行,比如发送短信/邮件、消息推送、音视频处理等等
- 定时任务:定时执行某件事情,比如每天数据统计
1.4、celery架构
Celery的架构由三部分组成:
- 消息中间件(message broker)
- 任务执行单元(worker)
- 任务执行结果存储(task result store)组成。
消息中间件:
celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成 。包括,RabbitMQ, Redis等等。
Broker 担当一个中间人的角色。在工头提出任务的时候,把所有的任务放到 Broker 里面,在 Broker 的另外一头,一群码农等着取出一个个任务准备着手做。这种模式注定了整个系统会是个开环系统,工头对于码农们把任务做的怎样是不知情的。所以我们要引入 Backend 来保存每次任务的结果。这个 Backend 有点像我们的 Broker,也是存储任务的信息用的,只不过这里存的是那些任务的返回结果。我们可以选择只让错误执行的任务返回结果到 Backend,这样我们取回结果,便可以知道有多少任务执行失败了。backend其实就相当于一个数据库
任务执行单元:
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中。
任务结果存储:
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP, redis等。
1.5、安装配置
- pip3 install celery
- 消息中间件:RabbitMQ/Redis
- app=Celery('任务名', backend='xxx', broker='xxx')
二、使用
2.1、基本使用
先创建一个celery_app_task.py,任务文件(所有要执行的任务写在这里),文件名可以随便取
from celery import Celery
"""1、指定broker(消息中间件)和backend(结果存储)"""
# redis没有密码时
broker = 'redis://127.0.0.1:6379/0'
backend = 'redis://127.0.0.1:6379/1'
# redis有密码时
# backend='redis://:123456@127.0.0.1:6379/1'
# broker='redis://:123456@127.0.0.1:6379/2'
"""2、实例化产生一个Celery对象,一定要指定名字"""
app = Celery('test', broker=broker, backend=backend)
"""3、写任务,并在函数(add)上加装饰器app.task绑定任务"""
# 任务其实就是函数
# 需要用一个装饰器装饰,表示该任务是被celery管理的,且可用celery执行
@app.task
def add(x, y):
import time
time.sleep(3)
return x+y
接下来创建一个提交任务的文件:add_task.py
import celery_app_task
# 正常同步执行任务
# res = celery_app_task.add(6, 6)
# print(res) # 这边等待sleep时间后,直接可以打印结果
# 提交任务到消息队列中(只是提交到队列,并没有执行)
res = celery_app_task.add.delay(6, 6)
print(res) # 立马提交任务,并返回了任务id:760473cf-9e62-4bae-b210-4cf381144ee5
接下来要启动worker去执行任务(celery_app_task是任务包的名字),需要安装eventlet模块:命令需要在项目路径执行
linux:celery worker -A celery_app_task -l info
windows:celery worker -A celery_app_task -l info -P eventlet
执行任务命令提交后,可以看到 提交的任务立即执行了
如果我们想要定时执行任务,让它们在一段时间之后再执行,该怎么做呢?
import celery_app_task
from datetime import datetime, timedelta
# 定时任务:5s后执行add任务
"""第一种获取时间"""
'''
v1 = datetime(2020, 1, 1, 00, 00, 00) # 可以固定设置一个时间执行任务
print(v1) # 2020-01-01 00:00:00
print(v1.timestamp()) # 将上面的时间转换为时间戳
v2 = datetime.utcfromtimestamp(v1.timestamp())
print(v2) # 2019-12-31 16:00:00
# 取出要执行任务的时间对象,调用apply_async方法,args是参数,eta是执行的时间
result = celery_app_task.add.apply_async(args=[1, 3], eta=v2)
print(result.id)
'''
"""第二种获取时间"""
v1 = datetime.now() # 获取实时时间
# 默认用utc时间
utc_time = datetime.utcfromtimestamp(v1.timestamp())
# print(v2) # 将时间戳转换为utc时间:2019-07-12 08:50:30
# 取5s后的时间对象
time_delay = timedelta(seconds=5) # 可以设置延时几天,几分,几秒后执行
task_time = utc_time + time_delay
# 取出要执行任务的时间对象,调用apply_async方法,args是参数,eta是执行的时间
res = celery_app_task.add.apply_async(args=[4, 3], eta=task_time)
print(res.id)
创建py文件:result.py,查看任务执行结果
from celery_app_task import app
from celery.result import AsyncResult
# 根据id拿到结果
async = AsyncResult(id='760473cf-9e62-4bae-b210-4cf381144ee5', app=app)
if async.successful():
# 取出return的结果
res = async.get()
print(res) # 12
# res.forget() # 删除结果
elif async.failed():
print('执行失败')
elif async.status == 'PENDING':
print('任务等待中被执行')
elif async.status == 'RETRY':
print('任务异常后正在重试')
elif async.status == 'STARTED':
print('任务已经开始被执行')
2.2、celery的多任务执行
项目结构:
pro_cel # 项目
├── celery_task # celery相关文件夹
│ ├── celery.py # celery连接和配置相关文件,必须叫这个名字
│ └── tasks1.py # 所有任务函数
│ └── tasks2.py # 所有任务函数
├── check_result.py # 检查结果
└── send_task.py # 触发任务windows启动执行任务:celery worker -A celery_task -l info -P eventlet
先来创建celery.py配置文件,一定要叫这个名字,因为启动的时候,直接写到文件夹一层,自动去找这个名字
下面的场景是每天的什么时候,或者每月的某个时候定时做某些工作。
比如:秒杀推送信息,每天某个点同步redis的数据到数据库等
from celery import Celery
from datetime import timedelta
from celery.schedules import crontab
app = Celery('celery_demo',
broker='redis://127.0.0.1:6379/1',
backend='redis://127.0.0.1:6379/2',
# 包含以下两个任务文件,去相应的py文件中找任务,对多个任务做分类
include=['celery_task.user_task',
'celery_task.order_task'
])
# 时区
# cel.conf.timezone = 'Asia/Shanghai'
# 是否使用UTC
# cel.conf.enable_utc = False
"""配置文件app.conf"""
app.conf.beat_schedule = {
# 名字随意命名
'add-every-10-seconds': {
# 执行tasks1下的test_celery函数
'task': 'celery_task.order_task.order_add',
# 每隔2秒执行一次
# 'schedule': 1.0,
# 'schedule': crontab(minute="*/1"),
'schedule': timedelta(seconds=2),
# 传递参数
'args': (5, 6)
},
"""crontab定时"""
# 'add-every-12-seconds': {
# 'task': 'celery_task.order_task.order_add',
# # 每年4月11号,8点42分执行
# 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
# # 'schedule': crontab(minute=42, hour=8, day_of_month=11, month_of_year=4),
# 'args': (16, 16)
# },
}
然后准备一下其他两个任务文件,这个根据自己的项目需求来看
"""user_task.py"""
import time
from celery_task.celery import app
@app.task
def user_add(x, y):
time.sleep(3)
return x+y
"""order_task.py"""
import time
from celery_task.celery import app
@app.task
def order_add(x, y):
time.sleep(3)
return x+y
然后提交任务的文件send_task.py
from celery_task.order_task import order_add
from celery_task.user_task import user_add
res = user_add.delay(10, 50)
print(res)
ret = order_add.delay(5, 10)
print(ret)
我上面的配置想要让程序每隔2秒提交一次任务,或者每年每天定时执行哪个任务,那么就需要先启动一个beat
celery beat -A celery_task -l info
然后启动任务执行(windows下,linux下不用-P后的参数):
celery worker -A celery_task -l info -P eventlet
最后查询结果的文件:check_result.py
from celery.result import AsyncResult
from celery_task.celery import app
async = AsyncResult(id="44648a25-42f7-4f00-acc2-28e1335b8a7b", app=app)
if async.successful():
# 取出它return的值
result = async.get()
print(result)
# result.forget() # 将结果删除,执行完成,结果不会自动删除
# async.revoke(terminate=True) # 无论现在是什么时候,都要终止
# async.revoke(terminate=False) # 如果任务还没有开始执行呢,那么就可以终
elif async.failed():
print('执行失败')
elif async.status == 'PENDING':
print('任务等待中被执行')
elif async.status == 'RETRY':
print('任务异常后正在重试')
elif async.status == 'STARTED':
print('任务已经开始被执行')
三、django中使用
django中虽然提供了django-celery,但是对于django和celery的版本的匹配度要求很高,所以我们还是使用原生的celery。
首先我们把之前的celery_task文件夹可以直接拷贝到我们项目的目录下使用,最主要还是那个celery.py配置,其他的任务根据项目需求来就行了。这里做测试,我还是拿之前的user_task.py和order_task.py文件
接下来我们去写视图
from django.shortcuts import HttpResponse
from celery_task.user_task import user_add
from celery.result import AsyncResult
from celery_task.celery import app
def index(request):
# 提交任务,返回id
res = user_add.delay(8, 8)
return HttpResponse(res.id)
def check_result(request):
res = request.GET.get('id')
async = AsyncResult(id=res, app=app)
if async.successful():
# 取出结果
result = async.get()
print(result)
return HttpResponse(result)
配好路由,就可以访问了,别忘了执行前面说的命令执行任务啊:celery worker -A celery_task -l info -P eventlet
然后访问check,拿到结果:
注意,在 任务中需要调用models查询数据库时,一定要导入,就想manage.py的那里一样,不然无法使用orm查询数据。
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled15.settings")
import django
django.setup()