• flask中使用celery


    Celery

    1.Celery是什么

    Celery 一个懂得 异步任务 , 定时任务 , 周期任务 的芹菜
    Celery 是基于Python实现的模块, 用于执行异步定时周期任务的
    其结构的组成是由
        1.用户任务 app
        2.管道 broker 用于存储任务 官方推荐 redis rabbitMQ  / backend 用于存储任务执行结果的
        3.员工 worker

    • 多任务异步任务: app---task---调度器(broker)---worker ---调度器(backend)---task---app
    • 定时任务:task---多少时间执行该任务>调度器(broker)---多少时间执行该任务>worker等待 ---调度器(backend)---task
    • 周期任务:

    2.Celery的简单实例

    # s1.py
    from celery import Celery
    import time
    #创建一个Celery实例,这就是我们用户的应用app
    my_task = Celery("tasks", broker="redis://127.0.0.1:6379", backend="redis://127.0.0.1:6379")
    # 为应用创建任务,func1
    @my_task.task
    def func1(x, y):
        time.sleep(15)
        return x + y
    
    
    # s2.py
    from s1 import func1
    # 将任务交给Celery的Worker执行
    res = func1.delay(2,4)
    #返回任务ID
    print(res.id)
    
    
    #s3.py
    from celery.result import AsyncResult
    from s1 import my_task
    # 异步获取任务返回值
    async_task = AsyncResult(id="55a84ea3-afa4-4ab9-8650-40e156c07441",app=my_task)
    # 判断异步任务是否执行成功
    if async_task.successful():
        #获取异步任务的返回值
        result = async_task.get()
        print(result)
    else:
        print("任务还未执行完成")
    
    
    """
    三个文件创建完成了,哪个文件是app,哪个文件是borker,哪个是worker了
    那我们得一步一步分析了,最终我们要执行的任务是在 s1.py 中,也就是worker需要执行的任务,所以worker就是 s1.py了
    现在我们就来启动worker,如何启动呢
    
    根据操作系统的不同,启动方式也存在差异:
    Linux - celery worker -A s1 -l INFO
    Windows:这里需要注意的是celery 4.0 已经不再对Windows操作系统提供支持了,也就是在windows环境下出现问题除非自己解决,否贼官方是不会给你解决的
    Windows - celery worker -A s1 -l INFO -P eventlet
    ps: eventlet 是一个python的三方库 需要使用 pip安装 pip install eventlet
    """

    """
    启动完成,其实在s1.py当中,worker已经知道了自己的broker 和 backend 在哪里了
    接下来就让异步任务开始执行吧,对了 s2.py 中就是使用 delay 的方式来开始执行的异步任务
    执行 s2.py 得到了一个字符串 55a84ea3-afa4-4ab9-8650-40e156c07441 这个字符串儿就是异步任务的ID
    在Celery worker 的控制台中可以看到这个样子
    """

    等待15秒钟之后就可以的到这样一个字符串 

    然后通过s3.py修改异步任务的ID来获取任务返回的结果
    这样就简单完成了一个Celery异步任务了

    3.Celery 结合 Flask 模拟"抢购"系统

    https://pan.baidu.com/s/15T08QUBYWqi8QjE0sPUBlg

    from flask import Flask,render_template,request
    from s1 import goods as task_goods
    from s2 import callback
    
    app = Flask(__name__)
    
    goods_list = [
        {"id":1,"title":"手机"},
        {"id":2,"title":"电脑"},
        {"id":3,"title":"电视"}
    ]
    
    @app.route("/")
    def index():
    
        return render_template("index.html",goods=goods_list)
    
    @app.route("/goods")
    def goods():
        good_id = request.args.get("id")
        result = task_goods.delay(good_id)
    
        return render_template("goods.html",task_id=result.id)
    
    @app.route("/res")
    def res():
        task_id = request.args.get("task_id")
        result = callback(task_id)
        return render_template("res.html",msg=result)
    
    if __name__ == '__main__':
        app.run()
    app.py
    from celery import Celery
    import time
    
    my_goods = Celery("task",broker="redis://127.0.0.1",backend="redis://127.0.0.1")
    
    @my_goods.task
    def goods(id):
        time.sleep(15)
        return f"抢购成功{id}"
    s1.py
    from celery.result import AsyncResult
    from s1 import my_goods
    
    def callback(task_id):
        async_task = AsyncResult(id=task_id,app=my_goods)
        if async_task.successful():
            return async_task.get()
        else:
            return "玩儿命抢购ing......"
    s2.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        {{ msg }}
    </body>
    </html>
    res.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>抢购</title>
    </head>
    <body>
    <form action="">
    
    </form>
    <table border="1px">
        <thead>
        <tr>
            <td>ID</td>
            <td>名称</td>
            <td>操作</td>
        </tr>
        </thead>
        <tbody>
        {% for good in goods %}
            <tr>
                <td>{{ good.id }}</td>
                <td>{{ good.title }}</td>
                <td><a href="/goods?id={{ good.id }}">购买</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
    </body>
    </html>
    index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        正在排队抢购中,<a href="/res?task_id={{ task_id }}">点击这里</a>查看抢购结果
    </body>
    </html>
    goods.html

    4.Celery项目目录

    在实际项目中我们应用Celery是有规则的

    要满足这样的条件才可以哦,目录Celery_task这个名字可以随意起,但是一定要注意在这个目录下一定要有一个celery.py这个文件

    1 from celery import Celery
    2
    3 celery_task = Celery("task",
    4                      broker="redis://127.0.0.1:6379",
    5                      backend="redis://127.0.0.1:6379",
    6                      include=["Celery_task.task_one","Celery_task.task_two"])
    7 # include 这个参数适用于寻找目录中所有的task
    celery.py
    1 from .celery import celery_task
    2 import time
    3
    4 @celery_task.task
    5 def one(x,y):
    6     time.sleep(5)
    7     return f"task_one {x+y}"
    task_one
    1 from .celery import celery_task
    2 import time
    3
    4 @celery_task.task
    5 def two(x,y):
    6     time.sleep(5)
    7     return f"task_two {x+y}"
    task_two

    这样Celery项目目录结构就已经做好了然后再 my_celery中调用

    1 from Celery_task.task_one import one
    2 from Celery_task.task_two import two
    3
    4 one.delay(10,10)
    5 two.delay(20,20)
    my_celery.py

    PS:启动Worker的时候无需再使用文件启动,直接启动你的Celery_task目录就行了
    celery worker -A Celery_task -l INFO -P eventlet
    这样celery就可以自动的去检索当前目录下所有的task了,通过Include这个参数逐一去寻找

    5.Celery定时任务

    我们还使用Celery_task这个示例来修改一下
    my_celery中进行一下小修改

     1 from Celery_task.task_one import one
     2 from Celery_task.task_two import two
     3
     4 # one.delay(10,10)
     5 # two.delay(20,20)
     6
     7 # 定时任务我们不在使用delay这个方法了,delay是立即交给task 去执行
     8 # 现在我们使用apply_async定时执行
     9
    10 #首先我们要先给task一个执行任务的时间
    11 import datetime,time
    12 # 获取当前时间 此时间为东八区时间
    13 ctime = time.time()
    14 # 将当前的东八区时间改为 UTC时间 注意这里一定是UTC时间,没有其他说法
    15 utc_time = datetime.datetime.utcfromtimestamp(ctime)
    16 # 为当前时间增加 10 秒
    17 add_time = datetime.timedelta(seconds=10)
    18 action_time = utc_time + add_time
    19
    20 # action_time 就是当前时间未来10秒之后的时间
    21 #现在我们使用apply_async定时执行
    22 res = one.apply_async(args=(10,10),eta=action_time)
    23 print(res.id)
    24 #这样原本延迟5秒执行的One函数现在就要在10秒钟以后执行了
    my_celery

    定时任务只能被执行一次,那如果我想每隔10秒都去执行一次这个任务怎么办呢? 周期任务来了

    6.Celery周期任务

    首先要对Celery_task中的celery.py进行一点修改:

     1 from celery import Celery
     2 from celery.schedules import crontab
     3
     4 celery_task = Celery("task",
     5                      broker="redis://127.0.0.1:6379",
     6                      backend="redis://127.0.0.1:6379",
     7                      include=["Celery_task.task_one","Celery_task.task_two"])
     8
     9 #我要要对beat任务生产做一个配置,这个配置的意思就是每10秒执行一次Celery_task.task_one任务参数是(10,10)
    10 celery_task.conf.beat_schedule={
    11     "each10s_task":{
    12         "task":"Celery_task.task_one.one",
    13         "schedule":10, # 每10秒钟执行一次
    14         "args":(10,10)
    15     },
    16     "each1m_task": {
    17         "task": "Celery_task.task_one.one",
    18         "schedule": crontab(minute=1), # 每一分钟执行一次
    19         "args": (10, 10)
    20     },
    21     "each24hours_task": {
    22         "task": "Celery_task.task_one.one",
    23         "schedule": crontab(hour=24), # 每24小时执行一次
    24         "args": (10, 10)
    25     }
    26
    27 }
    28
    29 #以上配置完成之后,还有一点非常重要
    30 # 不能直接创建Worker了,因为我们要执行周期任务,所以首先要先有一个任务的生产方
    31 # celery beat -A Celery_task
    32 # celery worker -A Celery_task -l INFO -P eventlet
    celery.py

    创建Worker的方式并没有发行变化,但是这里要注意的是,每间隔一定时间后需要生产出来任务给Worker去执行,这里需要一个生产者beat

    celery beat -A Celery_task  #创建生产者 beat 你的 schedule 写在哪里,就要从哪里启动

     celery worker -A Celery_task -l INFO -P eventlet

     创建worker之后,每10秒就会由beat创建一个任务给Worker去执行

  • 相关阅读:
    BZOJ2219数论之神——BSGS+中国剩余定理+原根与指标+欧拉定理+exgcd
    Luogu 3690 Link Cut Tree
    CF1009F Dominant Indices
    CF600E Lomsat gelral
    bzoj 4303 数列
    CF1114F Please, another Queries on Array?
    CF1114B Yet Another Array Partitioning Task
    bzoj 1858 序列操作
    bzoj 4852 炸弹攻击
    bzoj 3564 信号增幅仪
  • 原文地址:https://www.cnblogs.com/bubu99/p/13580669.html
Copyright © 2020-2023  润新知