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

2020-12-08 10:10:14 +08:00
 luxiaoer

现状: 原钉钉发送消息是同步程序,发送钉钉消息程序中有记录 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


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

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

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

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

实在想传 g 大概得用 pickle
luxiaoer
2020-12-08 15:30:05 +08:00
@SjwNo1
尝试过,没成功
luxiaoer
2020-12-08 15:30:38 +08:00
@qdzzyb
目前在这个方向努力

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/733193

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX