Celery
Celery
在一个应用服务中,对于时效性要求没那么高的业务场景,我们没必要等到所有任务执行完才返回结果,例如用户注册场景中,保存了用户账号密码之后,就可以立即返回,后续的账号激活邮件,可以用一种异步的形式去处理,这种异步操作可以用队列服务来实现。否则,如果等到邮件发送成功可能几秒过去了。
Celery 是什么?
Celery 是Python语言实现的分布式队列服务,除了支持即时任务,还支持定时任务,Celery 有5个核心角色。
Task
任务(Task)就是你要做的事情,例如一个注册流程里面有很多任务,给用户发验证邮件就是一个任务,这种耗时的任务就可以交给Celery去处理,还有一种任务是定时任务,比如每天定时统计网站的注册人数,这个也可以交给Celery周期性的处理。
Broker
Broker 的中文意思是经纪人,指为市场上买卖双方提供中介服务的人。在Celery中这个角色相当于数据结构中的队列,介于生产者和消费者之间经纪人。例如一个Web系统中,生产者是主程序,它生产任务,将任务发送给 Broker,消费者是 Worker,是专门用于执行任务的后台服务。Celery本身不提供队列服务,一般用Redis或者RabbitMQ来实现队列服务。
Worker
Worker 就是那个一直在后台执行任务的人,也成为任务的消费者,它会实时地监控队列中有没有任务,如果有就立即取出来执行。
Beat
Beat 是一个定时任务调度器,它会根据配置定时将任务发送给 Broker,等待 Worker 来消费。
Backend
Backend 用于保存任务的执行结果,每个任务都有返回值,比如发送邮件的服务会告诉我们有没有发送成功,这个结果就是存在Backend中,当然我们并不总是要关心任务的执行结果。
记住这5个角色后面理解Celery就轻松了。
这里面 异步任务、定时任务可以由各个服务器发起,定时任务可以由 celery 代理发起,woker 可以由多个 celery 处理。
异步任务
假设我们选择Redis作为broker。
- 创建
celery
实例,创建任务
# tasks.py
from celery import Celery
# 1. 创建celery 实例
app = Celery('tasks', broker='redis://localhost:6379', backend='redis://localhost:6379')
# 2. 创建任务
@app.task
def task1(args):
import time
# 模拟复杂操作
time.sleep(2)
return 'success'
- 启动worker
启动Worker,监听 Broker 中是否有任务,命令:celery worker
,你可能需要指定参数
-A: 指定 celery 实例所在哪个模块中,例子中,celery实例在tasks.py文件中,启动成功后,能看到信息
函数用app.task 装饰器修饰之后,就会成为Celery中的一个Task。
celery -A tasks worker --loglevel=info
- 调用任务
# user.py
from tasks import task1
def func():
task1.delay('任务开始')
if __name__ == '__main__':
func()
在主程序中,调用函数的.delay
方法
目录结构
── celery_test
├── tasks.py
└── user.py
运行 python user.py, 启动应用程序
注意:
- celery4 不支持windows,win环境选用celery3版本 或者安装 eventlet 模块 使用命令
celery -A <projectname> worker -l info -P eventlet
启动celery - 如果使用 python3.7 与 celery3 ,
kombu
中有async
关键字 与python3.7 冲突,需要将kombu
中 async 模块替换个名字(async_) ,并将源码中所有用到的地方全部替换
周期任务
import datetime
import time
from tasks import task1
def func2():
"""10s后调用任务"""
# 定时任务我们不在使用delay这个方法了,delay是立即交给task 去执行
# 现在我们使用apply_async定时执行
# 获取当前时间 此时间为东八区时间
ctime = time.time()
# 将当前的东八区时间改为 UTC时间 注意这里一定是UTC时间
utc_time = datetime.datetime.utcfromtimestamp(ctime)
# 为当前时间增加 10 秒
add_time = datetime.timedelta(seconds=10)
action_time = utc_time + add_time
# action_time 就是当前时间未来10秒之后的时间
# 现在我们使用apply_async定时执行
res = task1.apply_async(args=('func2',), eta=action_time)
print(res.id)
if __name__ == '__main__':
func2()
print(1)
直接运行此文件,就可以发送定时任务给 celery
定时任务
配置 CELERYBEAT_SCHEDULE
参数
CELERYBEAT_SCHEDULE = {
'add-every-3-seconds': {
'task': 'celery_app.task1.add',
'schedule': timedelta(seconds=3), # 每 3 秒执行一次
'args': (5, 8) # 任务函数参数
},
'multiply-at-some-time': {
'task': 'celery_app.task2.multiply',
'schedule': crontab(hour=9, minute=50), # 每天早上 9 点 50 分执行一次
'args': (3, 7) # 任务函数参数
}
}
执行命令
celery_demo $ celery -A celery_app worker --loglevel=info
celery_demo $ celery beat -A celery_app
# 或者
$ celery -B -A celery_app worker --loglevel=info
# 若修改代码,需要重启 worker 和 beat