如何在 Flask 的 Factory Mode 中使用 Celery? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mrchi
V2EX    Flask

如何在 Flask 的 Factory Mode 中使用 Celery?

  •  
  •   mrchi
    mrchi 2018-07-17 08:15:40 +08:00 6226 次点击
    这是一个创建于 2642 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,诉求是,能够在 Celery 中正常使用 Flask 的上下文。按照 Flask 文档 Celery Background Tasks 的示例,在 Celery App 创建和修改 Task 类的时候需要 Flask App。这就有一个死循环:

    • Celery task 定义时需要 Celery app 实例;
    • Celery app 创建时需要 Flask app 实例,需要获取配置,重写 Task 类已在 Flask 上下文中执行任务;
    • Flask app 是在运行时延迟创建的。

    不知道大家都是怎么做的? 我自己实现了一个丑陋的方法flask-with-celery-example,大家给给意见有没有漏洞或者 bug 啥的。


    刚刚想到另一种方法是:

    • 把 Celery APP 的配置独立出来,不使用 Flask 的配置,此时不需要 Flask App 作为参数创建 Celery App,可以正常的写任务;

    • 在 Flask App 创建的文件中,导入 Celery App,并重写 celeryapp.Task

    第 1 条附言    2018-07-17 14:28:40 +08:00

    刚刚试了一下 Celery 4.2.0版本,原来可以 先创建 Celery App,再通过配置更改 broker_url 的地址了。之前受了这篇文章的影响,以为不行呢。

    这样的话,其实 Celery 跟 Flask 的使用就没有大的冲突了,可以现在定义 Celery 任务的文件中创建 Celery App,然后在 Flask 的 Factory Function 中更新 Celery App 的配置以及重写 celeryapp.Task

    另外值得讨论的一个点是,要不要把Celery的配置放在Flask的配置当中。Celery在新版本(4.0+)中开始推荐使用全小写的配置(来源)。我个人觉得,将 broker_url,result_backend等配置可以放在Flask配置中(目前Celery还兼容大写配置项),方便不同环境使用不同的broker,防止任务混乱,其他的配置可以单独写文件存放了。

    20 条回复    2020-01-14 09:01:08 +08:00
    rogwan
        1
    rogwan  
       2018-07-17 08:31:29 +08:00 via Android
    看任务类型吧,如果必须要和 flask 结合,那只能用 flask 的 App 上下文进行绑定。如果只是一些独立的异步任务,是可以做成独立的 celery App。
    greyli
        2
    greyli  
       2018-07-17 09:00:00 +08:00   1
    1. 建议的处理方式:Celery 实例全局创建; Celery 配置加载和 Task 上下文重写在 Flask 工厂函数中进行。
    2. 新版本( 4 )的 Celery 中的配置采用小写形式,不能通过 Flask 的配置对象读取,所以建议将两者的配置分开处理。
    mrchi
        3
    mrchi  
    OP
       2018-07-17 09:19:18 +08:00
    @rogwan 要和 Flask 结合的一大原因是,用了 Flask-SQLAlchemy,操作数据库用到 db,所以不好独立。
    mrchi
        4
    mrchi  
    OP
       2018-07-17 09:24:16 +08:00
    @greyli 好的,我试一下
    hotea
        5
    hotea  
       2018-07-17 09:28:17 +08:00
    目前我是这样搞的,celery_app 在使用的时候创建,创建时传入的参数是一个新的 flask_app.

    https://paste.ofcode.org/ueVCWz355YudaPKa5NaqJE
    vZexc0m
        6
    vZexc0m  
       2018-07-17 09:35:02 +08:00
    可以独立于 flask, 可以使用 with application.app_context()提供上下文
    hotea
        7
    hotea  
       2018-07-17 09:37:13 +08:00
    上面这种方式有个注意点地方就是在视图中需要发送任务( tasks.foo.delay(*args, **kw),而在 import tasks 时,tasks 的导入语句要在蓝图之下,否则会循环导入。

    还有另外一种发送任务的方式:在视图函数里现用现生成 task_app, 然后 task_app.send_task('tasks.foo'), kwargs={})这种将任务执行函数以文本形式传入。使用起来不如第一种方便,但这种没有循环导入
    mrchi
        8
    mrchi  
    OP
       2018-07-17 10:31:11 +08:00
    @hotea 这种我看到过,如果使用自动化测试工具的话,会不会造成 Celery 应用的 Flask app 环境配置与实际运行的 Flask App 环境配置不一致呢
    mrchi
        9
    mrchi  
    OP
       2018-07-17 10:34:13 +08:00
    @vZexc0m 如果在每个 task 里写的话,感觉相当繁琐,如果重写 celeryapp.task,那么就要拿到 application 了。感觉 2#的方法不错的。
    GoLand
        10
    GoLand  
       2018-07-17 13:02:55 +08:00
    Celery 就不应该依赖 Flask App,也不用什么 flask-sqlalchemy,直接用 sqlalchemy 不可以么,另外这些配置都单独写,别全部绑在 flask app config 上。并且 celery 和 sqlalchemy 都全局实例化。这样就不存在什么问题了吧。另外,view 层就不应该直接调用 task 吧,再封装一层代理,view 层所有接口都走那层。

    https://github.com/Robpol86/Flask-Large-Application-Example

    可以看看这个组织方式。还不错。
    rogwan
        11
    rogwan  
       2018-07-17 13:28:36 +08:00 via Android
    @GoLand 有时候没办法,任务请求是从 flask 来的,必须要绑定 flask app
    mrchi
        12
    mrchi  
    OP
       2018-07-17 14:16:48 +08:00
    @GoLand flask-sqlalchemy 对 sqlalchemy 的一些封装,还是很好用的,比如 pagination。
    mrchi
        13
    mrchi  
    OP
       2018-07-17 14:29:59 +08:00
    @greyli

    - 将 broker_url,result_backend 等配置可以放在 Flask 配置中,方便不同环境使用不同的 broker 配置,防止任务混乱;
    - 其他的配置可以单独写文件存放。

    这样如何?
    est
        14
    est  
       2018-07-17 14:30:11 +08:00
    flask 为了 url 能 decorator 一个便利,制造了 n 多麻烦。LZ 的问题就是一例。
    brucedone
        15
    brucedone  
       2018-07-17 16:27:55 +08:00
    不推荐将类似 message queue 或者 task queue 与 flask 绑定 ,另外 celery 略重,可以考虑轻量级的 python-rq,方便好用,简单明了,等到了真正不满足的时候再考虑换 celery
    greyli
        16
    greyli  
       2018-07-17 19:31:28 +08:00
    @mrchi 这样也不错,不过据我所知,Flask 不会读取小写形式的配置变量,需要注意一下。
    sunhk25
        17
    sunhk25  
       2020-01-10 10:59:36 +08:00
    想问下楼主有没有什么方法把以下两个任务一块启动
    celery worker -A manage:add_app -l info
    celery worker -A manage:factorial_app -l info


    分开的话有以下错误
    KeyError: 'app.celerytasks.add.my_add'
    KeyError: 'app.celerytasks.factorial.my_factorial'
    mrchi
        18
    mrchi  
    OP
       2020-01-12 10:06:56 +08:00
    @sunhk25 不会吧,我当时测正常的,你是在哪个目录下执行的命令呢?
    sunhk25
        19
    sunhk25  
       2020-01-14 08:35:09 +08:00
    我是在项目根目录 flask-with-celery-example 下执行的命令
    ※有一点不同的是我是用 virtualenv 建的虚拟环境
    好像确实是应不是在同一个文件中引发的错误,查了一下
    好像需要配置加载项 app.config['imports']
    https://www.jianshu.com/p/807efde55d81
    sunhk25
        20
    sunhk25  
       2020-01-14 09:01:08 +08:00
    我用的 celeryV4
    添加任务的命令改了一下就可以了
    # @add_app.task(shared=False)
    @add_app.task
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2359 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 15:47 PVG 23:47 LAX 08:47 JFK 11:47
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86