介绍 celery
1.简介
Celery
是一个简单、灵活且可靠的,处理大量消息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。
2.引入使用背景
在程序的运行过程中,我们经常会碰到一些耗时耗资源的操作,为了避免它们阻塞主程序的运行,我们经常会采用多线程或异步任务。
比如:
- 例子 a: 在 Web 开发中,对新用户的注册,我们通常会给他发一封激活邮件,而发邮件是个 IO 阻塞式任务,如果直接把它放到应用当中,就需要等邮件发出去之后才能进行下一步操作,此时用户只能等待再等待。更好的方式是在业务逻辑中触发一个发邮件的异步任务,而主程序可以继续往下运行。
- 例子 b: 接口自动化测试平台中,当选择 N 条 case 后点击执行按钮,因为运行选择的多条 case 是一个比较耗时的过程,所以不可能在点击按钮后,页面一直处于等待运行结果的状态,那样会给用户一个页面卡住或者夯住的体验,所以运行用例的过程就应该是一个异步任务去处理。
3.结构图
Celery 是一个强大的分布式任务队列,它可以让任务的执行完全脱离主程序,甚至可以被分配到其他主机上运行。我们通常使用它来实现异步任务(async task)和定时任务(crontab)。它的架构组成如下图:
4.名词介绍
1、Task
Task就是你要做的事情,也称为任务,例如一个注册流程里面有很多任务,给用户发验证邮件就是一个任务,这种耗时任务可以交给Celery去处理,还有一种任务是定时任务,比如每天定时统计网站的注册人数,这个也可以交给Celery周期性的处理。
2、Broker
Broker 的中文意思是经纪人,指为市场上买卖双方提供中介服务的人。在Celery中它介于生产者和消费者之间经纪人,这个角色相当于数据结构中的队列。例如一个Web系统中,生产者是处理核心业务的Web程序,业务中可能会产生一些耗时的任务,比如短信,生产者会将任务发送给 Broker,就是把这个任务暂时放到队列中,等待消费者来处理。 消费者是 Worker,是专门用于执行任务的后台服务。Worker 将实时监控队列中是否有新的任务,如果有就拿出来进行处理。Celery 本身不提供队列服务,一般用 Redis 或者 RabbitMQ 来扮演 Broker 的角色。
3、Worker
Worker 就是那个一直在后台执行任务的人,也称为任务的消费者,它会实时地监控队列中有没有任务,如果有就立即取出来执行。
4、Beat
Beat 是一个定时任务调度器,它会根据配置定时将任务发送给 Broker,等待 Worker 来消费。
5、Backend
Backend 用于保存任务的执行结果,每个任务都有返回值,比如发送邮件的服务会告诉我们有没有发送成功,这个结果就是存在Backend中,当然我们并不总是要关心任务的执行结果
使用讲解
步骤大致如下:
- 创建 Celery 实例
- 定义 worker
- 启动 broker (redis 或者 rabbitmq)
- 将定义的 worker 监听 broker
手动发送task执行异步任务
创建 Celery 实例及定义 worker 以及申明 broker(这里 broker 以 Redis 举例 )
# tasks.py
import time
from celery import Celery
# 申明获取task来源以及消费者执行task结果的redis
broker = 'redis://127.0.0.1:6379/1'
backend = 'redis://127.0.0.1:6379/2'
app = Celery('my_task', broker=broker, backend=backend)
@app.task
def add(x, y):
print(f"开始执行时间为:{time.time()}")
time.sleep(5) # 模拟耗时操作
print(f"结束执行时间为:{time.time()}")
return x + y
@app.task
def push_email(name):
print(f"准备给 {name}发送邮件!")
time.sleep(5)
print(f"邮件发送完成!")
return True
# 定义生产者 producer.py
from tasks import add, push_email
import random
def do_add(x, y):
res = add.delay(x, y)
print(res.id)
return {"code": 200, "mag": "success"}
def registered():
name = input("请输入注册人员名称:")
push_email.delay(name)
return {"code": 200, "msg": f"恭喜{name}注册成功! 请查看邮箱点击确认链接进行校验。"}
if __name__ == '__main__':
# 这里产生 100 个 task;
for i in range(100):
print(do_add(random.randint(1, 1000000), random.randint(1000000, 2000000)))
# 这里再新加一个 task
print(registered())
可以中途查看 broker 中的剩余任务个数
# test_redis.py (查看中间件中的剩余任务个数)
from redis import Redis
r = Redis(host="127.0.0.1", port=6379, db=1)
# r = Redis(host="10.50.255.104", port=6379, db=1)
task_list = r.lrange("celery", 0, -1)
print(f"任务个数:{len(task_list)}")
# for i in task_list:
# print(i)
- 启动 broker
备注:
- 这里生产者或消费者可以和 broker 部署在不同机器上,只要保证部署的机器彼此能够正常通信即可,本例是在都部署在本地机器上!
机器安装redis
或者rabbitmq
步骤不做详述。 - 使用该生产者模型时切记几者的启动顺序:
- 首选应该先启动 broker ,它是接受任务(task)的对象,如果不首先启动直接调用生产者或者定时任务,那么任务就不会出现在 broker 中,消费者也就没办法进行消费;
- 其次启动消费者并监听
broker
,这样只要broker
中出现任务task
, 消费者就会立即进行消费; - 上述两步完成就可以启动生产者进行对
broker
中分发任务,当然也可以通过定时任务分发任务;
redis-server /usr/local/etc/redis.conf
- 将定义的 worker 监听 broker
celery worker -A tasks -c 5 -l info
补充说明:
- tasks 代表的是 定义 woker 的文件名称,注意不带文件后缀;
- -c 5 代表的是指定执行队列任务子进程数,不指定时默认为 CPU 内核数;
- 执行生产者即可给 broker 发送 task 任务指令;
python producer.py
至此框架基础使用就已经介绍完毕,下面有一个demo
代码做演示
- 我们首先按照上述流程把框架搭建起来并启动 redis;
- 然后执行上述生产者代码;
- 执行查看 redis 中任务代码;
这里肯定有人问生产者不是一共分发了100+1 个任务吗?为啥redis
中查询结果只有100个?其实在执行生产者代码后,额外加1的那个task还没有被扔到redis
中,看下图即可:
第101个task的代码还没走完,需要输入名称后,该task才会生成;这里我们输入科比
再去调用查询结果如下:
- 我们最后在执行消费者,保证
woker
去监听broker
,这样生产者产生的任务就会被消费掉;
这里就能看到有同时有5个worker
去消费任务,直到所有的任务被执行完成;
补充:可以使用flower
库查看每个task的执行情况
使用命令:
celery -A tasks flower