一、Celery的介绍
Celery 是一个简单、灵活且可靠的,处理大量消息的分布式系统。官方文档为http://docs.celeryproject.org/en/latest/。
推荐这位老哥转的微博https://blog.csdn.net/cuomer/article/details/81214438
1、Celery的特点:
Celery是用Python语言写的,接口支持多种语言。主要有以下特点:
- 异步实时处理
- 支持扩展
- 支持多种消息队列
- 使用AMQP协议
2、Celery应用场景:
- 后台任务
- 异步操作
- 定期执行的工作
- 一定程度上的分布式计算
3、核心关键词
- Task queue:任务队列,描述在多线程或主机间任务工作的一种机制
- Task:任务,队列输入,工作单元
- Broker:消息代理,转发任务
- Worker:工作者,负责执行任务
4、工作流程:
http://erdemozkol.com/images/celery_schema.png
task ->MQ ->worker ->db
task是通过app来的,app是通过Celery类来的
5、在linux上安装
因为celery最新版本可能不支持在windows上运行,所以我们在linux上安装。
pip install celery
6、使用Celery前要注意的事项
Linux上检查与操作
-检查MQ, redis是否正常运行:MQ是消息代理:用来任务队列的分发。redis是数据库:用来保存处理后的数据
- 启动rabbitmq: systemctl start rabbitmq-server
- 查看rabbitmq状态: rabbitmqctl status
- 启动redis服务: systemctl start redis
- 查看redis是否启动: ps aux |grep redis
- pip install redis,这是用于保证Python连接redis
注意,上面有两个redis,第一个是启动redis服务,第二个是产生redis目录来存放处理后的数据,不要搞混
7、celery工作流程: task ->MQ ->worker ->db
①-创建task
②-启动celery服务
celery worker -A task -l info
出现一堆蓝色文字就证明启动成功,在这些文字里我们可以看到app名,所用的消息队列,结果放在哪里等信息。
③-执行worker任务
重新打开一个xshell窗口,进入task的父目录运行ipython
-worker.delay():延迟一会再执行任务
worker.delay()
-ready:判断当前任务有没有执行成功
在delay的基础上,即w=worker.delay(),w.ready()
-get:获取结果
在delay的基础上,即w=worker.delay(),w.get()
8、Celery练习目录
- 发送任务
- 定时执行任务
- 多台机器并发工作
9、练习代码
练习1:简单的发送任务,练习一共需要两个文件,task文件和run文件
task文件代码
1 # task文件代码:
2 import time
3 from celery import Celery
4
5 # 创建app
6 app = Celery("task", broker="amqp://", backend="redis://localhost")
7
8 # 创建worker,实际上就是做任务的文件
9 @app.task
10 def worker(name):
11 print(f"{name}工作正在进行")
12 time.sleep(2)
13 return f'{name}-ok'
14
"""
task是通过app来的,app是通过Celery类来的
创建task的语句:app = Celery(task名,broker,backend)
task需要有个固定的名字,一般来说和这个文件的名字一样,这样好管理
broker的意思是消息队列的地址,相当于流程里的MQ。标准格式为broker=“amqp://user:pwd@hostname:port/vhost”,“vhost“虚拟主机。本机可以简>略写为broker="amqp://",这样程序就会自动查找本地符合要求的amqp
backend是任务完成后数据的存储地址,相当于流程里的db。以redis为例,标准格式为backend="redis://:password@hostname:port/db-name",本机可以简略写为backend="redis://localhost
worker需要用到装饰器@app.task
celery有自己的执行命令,用celery worker -h查询
"""
可以用ipython来实现发送任务,输出的结果能在运行worker的那个窗口上查看到
In [1]: from task import worker
In [2]: w = worker.delay('log')
In [3]: w.ready()
Out[3]: True
In [4]: w.get()
Out[4]: 'log-ok'
还可以写一个run.py的脚本文件来实现发送任务,输出的结果能在运行worker的那个窗口上查看到
1 from task import worker
2 # 从同级目录里导入写好的task文件的worker类
3
4 # 1、delay()
5 # 2、ready()
6 # 3、get()
7
8 def run(name):
9 w = worker.delay(name)
10 while not w.ready():
11 pass
12 result = w.get()
13 print(result)
14 return result
15
16 run("log")
17 run("image")
18 run("doc")
练习2:定时执行任务,这个练习需要两个文件,config.py和task_time.py
练习一仅仅是一个简单的任务发送功能,其实celery还有定时的功能,比如15秒后做这个任务,这就需要添加任务配置文件,文件名一般为config.py
config.py配置文件代码
1 # config.py配置文件代码
2
3 from datetime import timedelta
4 # timedelta是用于隔多长时间运行一次的功能
5
6 CELERYBEAT_SCHEDULE = {
7 "every-15s-log": {
8 "task": "task_time.worker",
9 "schedule":timedelta(seconds=15),
10 "args":("log",)
11 }
12 }
13
"""
1、 配置文件里首先要导入需要的库
2、 CELERYBAT_SCHEDULE这个字典的名字是固定的,一定要用这个名字。
3、 这个字段是执行任务的标准。这个字典的第一个key是你想要做的事情,名字随意但是要有可读性,比如我的这个名字就是每15秒执行一次。
4、 第一个value也是一个字典,在这个字典里第一个key是"task",value是目标文件[filename].[funcname]。第二个key是"schedule",表示定时。seconds=15表示每15秒执行一次任务
5、 配置文件到此写完,可以保存退出
"""
task_time.py代码
1 # task_time.py
2 import time
3 from celery import Celery
4
5
6 # 创建app
7 app = Celery("task_log", broker="amqp://", backend="redis://localhost")
8 app.config_from_object("config")
9
10 # 创建worker,实际上就是做任务的文件
11 @app.task
12 def worker(name):
13 print(f"{name}工作正在进行")
14 time.sleep(2)
15 return f'{name}-15s-ok'
16
"""
编写任务文件,这里的任务文件就是task_time,在创建app的时候修改任务名,然后在用app.config_from_object("config.py")绑定配置文件
"""
这只是配置文件和任务文件写完了,配置文件里的CONFIGBEAT的意思是多长时间执行一次,也可以认为是心脏多长时间跳一次,但是什么时候开始跳不清所以还需要beat一下。一定要记住先worker,再beat 。打开一个xshell窗口worker一下,然后打开另一个xshell窗口。进入与worker的父目录beat一下:celery beat -A task_time -l info,就可以在运行worker的窗口看到每15秒便输出一个结果
小知识:
1、有一点需要关注,因为worker和beat都是全屏工作的,这就意味着需要两个窗口,窗口一旦关闭,这个效果就停止了。但是有一种方法可以关闭窗口但是任务依旧继续,这就是在运行的时候在命令后面加-D:celery beat -A task_time -l info -D,这样窗口不显示任务执行的效果,而是在后台进行,用ps aux | grep celery来查看celery的状况。
2、强制杀掉后台运行的进程(其实就是杀掉这个服务的进程ID):
找到进程ID:ps aux | grep ’celery worker' | awk '{print $2}'
注意此时查出来的进程里还包括查询进程ID这个语句所占用的进程,这个进程我们是不需要的,所以把这个进程过滤掉: ps aux | grep ’celery wor ker'| grep -v grep | awk '{print $ 2}'
杀掉进程;ps aux | grep 'celery worker' | grep -v grep | awk '{print $2}' | xargs kill
3、celery还有一个功能,就是定时做什么事,比如每周一7点半做任务等等。在config.py文件里导入库from celery.schedules import crontab,然后"schedule":crontab(hour=7,minite=30, day_of_week=1)。中国是东八区,所以需要设置一下时区,在CELERYBEAT_SCHEDULE前面加上语句C ELERY_TIMEZONE = "Asia/Shanghai",代码如下:
1 # config.py配置文件代码
2 from datetime import timedelta
3 from celery.schedules import crontab
4 # timedelta是用于隔多长时间运行一次的功能
5
6 CELERY_TIMEZONE = "Asia/Shanghai"
7 CELERYBEAT_SCHEDULE = {
8 "every-15s-log": {
9 "task": "task_time.worker",
10 "schedule":crontab(hour=7,minite=30, day_of_week=1),
11 "args":("log",)
12 }
13 }
练习3:多台机器并发工作,一共用到三个文件,外机tast.py,主机task.py和主机run.py
Celery支持一定程度上的分布式操作,即将任务分布给多台计算机共同完成,这里举例两个机器完成任务,其中一个机器既负责发任务,又负责承担一部分任务。假设主机ip地址为192.168.226.133, 外机ip地址为192.168.226.134
多台机器连接同一个主机的时候,会出现一些问题,以下是解决办法:
问题:主机启动redis时,默认地址为170.0.0.1,这是本地地址,不可以用在联网服务上的,外来机器也连接不上。所以需要更改。
解决办法:
①在主机上:vim /etc/redis.conf进入配置文件,注意修改之前需要备份一下。在命令模式下键入/bind来搜索,敲击n是搜索下一个目标。一直寻找到bind 127.0.0.1为止,把这句话注释起来。敲击n继续寻找,找到protected-mode yes,这句话的意思是保护模式,外网可以连接但是不能操作,我们要修改他,把yes改为no。搜索/password,找到附近有requirepass foobared为止(480行左右),把requirepass foobared这句话的注释放掉。删除foobared并写上自己的密码(相当于第一次设置密码,可以随便写,主要是为了安全性)。退出保存文件。重启redis服务:systemctl restart redis.
修改完redis配置,主机的任务文件里创建app的时候也需要修改backend,格式为"redis://:[password]@[主机ip]"
②主机修改完了,外机的连接也需要修改。
a:外机的任务文件里创建app的时候也需要修改backend,格式为"redis://:[password]@[主机ip]"
b:外机的redis连接主机的redis
第一种方法:启动redis服务同时连接到主机的redis:redis-cli -h [主机ip],连接上redis后可以看到,虽然连接但是不能操作,这时候继续在redis服务里键入:auth password(刚刚在主机的redis.conf里设置的password)就可以操作了。
第二种方法;redis-cli -h 192.168.226.130 -a password 也可以直接连接
在外机上编写task.py文件:
# 这个文件写的是134这个机器完成133主机分配的任务
import time
from celery import Celery
"""
task写的是主机133的任务task,这样能保证连接的是同一个任务.broker和backend都是主机133的配置。
这时候broker和backend就不能简写了,需要把用到的信息尽可能的写全。
broker=“amqp://user:pwd@hostname:port/vhost”,“vhost“虚拟主机。
backend="redis://:password@hostname:port/db-name"
"""
app = Celery("task", broker="amqp://[CeleryUsername]:[CeleryPassword]@[主机地址]//", backend="redis://:[RedisPassword]@[主机地址]:6379/0")
# 这个worker是在134这个机器上工作的
@app.task
def worker(name):
print(f"{name} work on 133")
time.sleep(1)
return "133's result"
run.py代码
1 from task import worker
2 # 从同级目录里导入写好的task文件的worker类
3
4 # 1、delay()
5 # 2、ready()
6 # 3、get()
7
8 def run(name):
9 w = worker.delay(name)
10 while not w.ready():
11 pass
12 result = w.get()
13 print(result)
14 return result
15
16 def main():
17 n = 5
18 while n > 0:
19 run("log")
20 run("image")
21 run("doc")
22 n -= 1
23
24 if __name__ == "__main__":
25 main()
此时在外机启动worker,在主机上也启动worker,再打开主机的另一个xshell窗口,运行run.py就可以在两个worker里看到两个机器在同时工作。