V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
luxiaoer
V2EX  ›  Python

请教 Flask 离线程序中如何利用上下文传递 g 和 request 参数

  •  
  •   luxiaoer · 2020-12-08 10:10:14 +08:00 · 3140 次点击
    这是一个创建于 1444 天前的主题,其中的信息可能已经有所发展或是发生改变。

    现状: 原钉钉发送消息是同步程序,发送钉钉消息程序中有记录 Flask 程序运行日志,日志包含当前请求用户信息,和请求参数信息。

    问题:Flask 调用 Celery,Celery 执行程序后发送钉钉消息,钉钉消息程序中记录日志。

    概要代码如下

    # __init__.py
    def create_app():
        ....
        return app
    
    # mycelery.py
    def make_celery(app):
        celery = Celery(__name__, broker=celeryconfig.broker_url, backend=celeryconfig.result_backend)
        celery.config_from_object(celeryconfig)
        class ContextTask(celery.Task):
            def __call__(self, *args, **kwargs):
                with app.app_context():
                    return self.run(*args, **kwargs)            
        celery.Task = ContextTask
        return celery
    
    
    app = Flask(__name__)
    celery_app = make_celery(app)
    
    
    # celery_task.py
    @celery_app.task(bind=True,ignore_result=True,max_retries=5)
    def celery_stock_sync(self,**kwargs):
        pass
    
    
    # dd_utils.py
    from record_log import run_log
    def send_dd_msg():
        res = dd_sdk.request()
        run_log(res)
        pass
    
    
    # record_log.py
    def run_log():
        # 这里需要记录当前操作的用户信息,以及用户请求信息
        user_id = g.get('user_id',None)
        method= request.method
        pass
    
    
    
    28 条回复    2020-12-10 10:16:49 +08:00
    coolair
        1
    coolair  
       2020-12-08 10:28:53 +08:00
    with app.app_context():
    # ...
    luxiaoer
        2
    luxiaoer  
    OP
       2020-12-08 10:40:11 +08:00
    @coolair
    求详细知道
    是否在 run_log 函数中 with app.app_context()
    app 有是如何导入的
    如果是 app 是如何导入,如果 from app import create_app 会提示循环导入
    如果从 Flask(__name__) g 为 None request 会提示 Working outside of request context.

    另外我在 celery_task 中也尝试 with app.app_context(),g 也是 None
    echowuhao
        3
    echowuhao  
       2020-12-08 10:51:21 +08:00 via Android
    你应该换方式 把需要得信息在 context 里面提取出来 传给 celery 而不是在 celery 中获取
    luxiaoer
        4
    luxiaoer  
    OP
       2020-12-08 10:58:28 +08:00
    @echowuhao
    可以是可以,但是这种方式不够解藕
    如果后续需要添加新的字段,就需要整个调整。
    关键是,原来的 run_log 就需要重写,就不能用 g 和 request 。相关的调用程序也需要重写吧
    rimutuyuan
        5
    rimutuyuan  
       2020-12-08 11:06:54 +08:00
    copy.deepcopy 可以实现么
    echowuhao
        6
    echowuhao  
       2020-12-08 11:14:39 +08:00 via Android
    @luxiaoer 代码要尽量少用 global 的东西 refactor 你的代码 object 进入 celery 都要序列化一个来回 费时费力
    luxiaoer
        7
    luxiaoer  
    OP
       2020-12-08 11:22:03 +08:00
    @echowuhao
    我们外部供应商给我们做外包,动不动就重构
    我自己写应用,有时候也动不动就重构
    但是自己挺反感重构的,怕缺陷。
    个人 觉得用 g 变量 和上下文是件很酷的事情 😂
    不过还是谢谢,提供一个思路
    echowuhao
        8
    echowuhao  
       2020-12-08 11:24:21 +08:00
    @luxiaoer 你们单元测试咋写的。应该把每个函数的输入最小化,尽量减少 global,否则我不知道你们咋做测试的。
    no1xsyzy
        9
    no1xsyzy  
       2020-12-08 11:25:16 +08:00
    因为 flask 是用的 thread local
    g 和 request 信息限定在当前 thread 内有效。
    luxiaoer
        10
    luxiaoer  
    OP
       2020-12-08 11:26:12 +08:00
    @rimutuyuan
    copy 还是 参数传递 方式,就还是后续调整会觉得麻烦。
    我朝着这个方向也思考思考
    大家 都不用 g 和上下文么,不酷么😂
    luxiaoer
        11
    luxiaoer  
    OP
       2020-12-08 11:27:00 +08:00
    @no1xsyzy
    但是可以用上下文不是么。
    echowuhao
        12
    echowuhao  
       2020-12-08 11:31:18 +08:00
    celery 你可以理解为对当前程序任务的外包。

    外包的时候,你们会把现在公司的所有数据都送给他们,让他们方便么。只给最少能完成任务的信息。

    这个在原理上没有任何问题。外包的人说,把你们的数据都给我,多方便,你干么。
    luxiaoer
        13
    luxiaoer  
    OP
       2020-12-08 11:54:27 +08:00
    @echowuhao
    有道理,那我暂且去重构了
    仍然期待上下文的解决方案
    kaneg
        14
    kaneg  
       2020-12-08 12:13:44 +08:00 via iPhone
    flask 上下文只对当前线程有效,celery 是在新线程中异步执行,是无法获取当时 de 信息。
    你要做的时触发 job 时把当前需要传递的信息传给 celery 。
    luxiaoer
        15
    luxiaoer  
    OP
       2020-12-08 12:15:25 +08:00
    @kaneg
    感谢解惑
    no1xsyzy
        16
    no1xsyzy  
       2020-12-08 12:31:33 +08:00
    @luxiaoer app.app_context() 是建立了一个 dummy 上下文,里面就是些假数据。
    SjwNo1
        17
    SjwNo1  
       2020-12-08 13:32:42 +08:00
    flask 线程隔离的
    可以 app._get_current_object() 传给 task
    qdzzyb
        18
    qdzzyb  
       2020-12-08 14:07:17 +08:00
    flask 在一个进程里,celery 的 worker 在另外一个进程里
    celery worker 启动以后的 app 对象跟 flask 里的 app 对象不是同一个东西。

    3 楼已经的做法已经是最好的办法了, 后续添加新字段 worker 的处理函数还得处理

    实在想传 g 大概得用 pickle
    luxiaoer
        19
    luxiaoer  
    OP
       2020-12-08 15:30:05 +08:00
    @SjwNo1
    尝试过,没成功
    luxiaoer
        20
    luxiaoer  
    OP
       2020-12-08 15:30:38 +08:00
    @qdzzyb
    目前在这个方向努力
    luxiaoer
        21
    luxiaoer  
    OP
       2020-12-08 15:31:00 +08:00
    @no1xsyzy
    好的,谢谢
    nthhdy
        22
    nthhdy  
       2020-12-08 15:38:59 +08:00
    传 g 和 request 不合理,同意 @echowuhao 的方法
    Cookieeeeee
        23
    Cookieeeeee  
       2020-12-08 19:34:41 +08:00
    把具体的信息取出来 celery 里头上下文和 flask 里头不一样 传对象取不到值
    Cookieeeeee
        24
    Cookieeeeee  
       2020-12-08 19:35:36 +08:00
    或者序列化下传进去解耦
    goxy
        25
    goxy  
       2020-12-08 22:04:11 +08:00
    需要理解一下 context 的概念

    Celery 和 Flask 都是独立设计的,你可以理解为两个职业不同的人在一起协同工作,A 要分配任务给 B,是需要通过发一份邮件之类的东西给 B,而不是把 A B 缝起来让他们成为连体婴儿。g 和 request 都是设计在 flask 内部的,对于 celery 是透明的,而开发上要做的是设计这样一份 邮件
    cz5424
        26
    cz5424  
       2020-12-08 22:24:08 +08:00
    启动任务把参数传进去就好了,不要用全局,他们都是互相独立的进程
    cz5424
        27
    cz5424  
       2020-12-08 22:26:04 +08:00
    不要觉得 g 和上下文变量很酷,声明多了你都不知道声明了多少个全局变量。
    luxiaoer
        28
    luxiaoer  
    OP
       2020-12-10 10:16:49 +08:00
    感谢以上的提醒,已经用了 3 楼的思路重写中。
    重写一半又准备重写了,准备把日志记录功能拆解到 Celery 中做异步日志记录😂
    每次都不想重写,然鹅一次次重写
    @Cookieeeeee
    @goxy
    @cz5424
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5709 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 01:46 · PVG 09:46 · LAX 17:46 · JFK 20:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.