Python学习-----Celery分布式消息队列

一、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里看到两个机器在同时工作。

你可能感兴趣的:(python)