
现状: 原钉钉发送消息是同步程序,发送钉钉消息程序中有记录 Flask 程序运行日志,日志包含当前请求用户信息,和请求参数信息。
# __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 1 coolair 2020-12-08 10:28:53 +08:00 with app.app_context(): # ... |
2 luxiaoer OP @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 |
3 echowuhao 2020-12-08 10:51:21 +08:00 via Android 你应该换方式 把需要得信息在 context 里面提取出来 传给 celery 而不是在 celery 中获取 |
4 luxiaoer OP @echowuhao 可以是可以,但是这种方式不够解藕 如果后续需要添加新的字段,就需要整个调整。 关键是,原来的 run_log 就需要重写,就不能用 g 和 request 。相关的调用程序也需要重写吧 |
5 14v45mJPBYJW8dT7 2020-12-08 11:06:54 +08:00 copy.deepcopy 可以实现么 |
6 echowuhao 2020-12-08 11:14:39 +08:00 via Android @luxiaoer 代码要尽量少用 global 的东西 refactor 你的代码 object 进入 celery 都要序列化一个来回 费时费力 |
7 luxiaoer OP @echowuhao 我们外部供应商给我们做外包,动不动就重构 我自己写应用,有时候也动不动就重构 但是自己挺反感重构的,怕缺陷。 个人 觉得用 g 变量 和上下文是件很酷的事情 不过还是谢谢,提供一个思路 |
9 no1xsyzy 2020-12-08 11:25:16 +08:00 因为 flask 是用的 thread local g 和 request 信息限定在当前 thread 内有效。 |
10 luxiaoer OP |
12 echowuhao 2020-12-08 11:31:18 +08:00 celery 你可以理解为对当前程序任务的外包。 外包的时候,你们会把现在公司的所有数据都送给他们,让他们方便么。只给最少能完成任务的信息。 这个在原理上没有任何问题。外包的人说,把你们的数据都给我,多方便,你干么。 |
14 kaneg 2020-12-08 12:13:44 +08:00 via iPhone flask 上下文只对当前线程有效,celery 是在新线程中异步执行,是无法获取当时 de 信息。 你要做的时触发 job 时把当前需要传递的信息传给 celery 。 |
17 SjwNo1 2020-12-08 13:32:42 +08:00 flask 线程隔离的 可以 app._get_current_object() 传给 task |
18 qdzzyb 2020-12-08 14:07:17 +08:00 flask 在一个进程里,celery 的 worker 在另外一个进程里 celery worker 启动以后的 app 对象跟 flask 里的 app 对象不是同一个东西。 3 楼已经的做法已经是最好的办法了, 后续添加新字段 worker 的处理函数还得处理 实在想传 g 大概得用 pickle |
23 Cookieeeeee 2020-12-08 19:34:41 +08:00 把具体的信息取出来 celery 里头上下文和 flask 里头不一样 传对象取不到值 |
24 Cookieeeeee 2020-12-08 19:35:36 +08:00 或者序列化下传进去解耦 |
25 goxy 2020-12-08 22:04:11 +08:00 需要理解一下 context 的概念 Celery 和 Flask 都是独立设计的,你可以理解为两个职业不同的人在一起协同工作,A 要分配任务给 B,是需要通过发一份邮件之类的东西给 B,而不是把 A B 缝起来让他们成为连体婴儿。g 和 request 都是设计在 flask 内部的,对于 celery 是透明的,而开发上要做的是设计这样一份 邮件 |
26 cz5424 2020-12-08 22:24:08 +08:00 启动任务把参数传进去就好了,不要用全局,他们都是互相独立的进程 |
27 cz5424 2020-12-08 22:26:04 +08:00 不要觉得 g 和上下文变量很酷,声明多了你都不知道声明了多少个全局变量。 |
28 luxiaoer OP 感谢以上的提醒,已经用了 3 楼的思路重写中。 重写一半又准备重写了,准备把日志记录功能拆解到 Celery 中做异步日志记录 每次都不想重写,然鹅一次次重写 @Cookieeeeee @goxy @cz5424 |