V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mrchi
V2EX  ›  Flask

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

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

    如题,诉求是,能够在 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   ·   实用小工具   ·   2495 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 04:43 · PVG 12:43 · LAX 20:43 · JFK 23:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.