简介
对于长时间执行的任务,我们不可能将其放在一个请求中完成,通常会用其他的方式异步执行。 队列则就是这样一个专门提供非阻塞任务执行的系统组件。在开发过程中我需要对其异步执行的任务进行测试,由于Celery没有提供Inprocess的API,所以不能直接在测试、开发的时候调用需要一番Tricky手段。由于Celery需要阻塞线程执行,无法直接作为后台任务,我们还需要在应用开始时,将其使用另外一个线程开启。
Flask Auto Reloader
Flask_ 的开发者模式(准确的来说是 WerkZeug_ 作为基础类库提供的)提供了许多工具,比如基于页面的Debuger提供网页版的Shell。但是开发模式会默认将auto_reloader
开启,好处是不用在修改逻辑后手工重启,坏处是他不是直接启动WebServer,而是从子线程中执行WebServer后,主线程执行一个本地文件Watcher,在文件发生变动时重启。
下图为调试模式下执行逻辑
在CheckAndReloader
过程中,一旦发现有.py[co]文件发生变更,则另外一个子进程,原进程退出。
为了复用逻辑,首次运行时不会直接执行 WebServer 部分,而是设置好环境变量WERKZEUG_RUN_MAIN
后,进入sMain
流程。下面为WerkZeugserving.py
部分代码:
def run_with_reloader(main_func, extra_files=None, interval=1): """Run the given function in an independent python interpreter.""" import signal signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': thread.start_new_thread(main_func, ()) try: reloader_loop(extra_files, interval) except KeyboardInterrupt: return try: sys.exit(restart_with_reloader()) except KeyboardInterrupt: pass
问题
麻烦在于,每一个在 Module 构建时创建对象,都会因为子进程的原因创建两次。
解决
不过既然知道了问题,我们屏蔽掉第一次执行母进程时的相关创建逻辑就行。接下来问题就转换为如何判断当前进程是否为子进程的问题。 方法有很多,比如根据系统环境变量来WERKZEUG_RUN_MAIN
来判断。或者比较父进程组ID,因为子进程所属于父进程组,所以可以根据如下方法判断。
# avoid Werbzeg's `run(use_reloader=True)` will create subprocessif os.getppid() != os.getpgrp(): __start_celelry_worker()
Conclusion
虽然解决了 Module 作用域下变量创建在WerkZeug调试模式中创建两次的问题,但是就问题本身来说解决的很不优美。Flask插件通常会绑定到Flask.app
创建之后,通过一个工厂方法调用。项目中的例子不是标准的 Flask 插件,所以会发生这类问题。如果调试模式增加一个 Hook 用于*正常逻辑*的执行也能解决这个问题。究其原因毕竟时基于 Flask 应用模块生命周期问题,Flask 已经提供了一套严禁的API,按照它提供的思路执行就是。所谓逆我者亡,也只能怪自己只猜中这开头,猜不中这结局。
很多同学的Sqlalchemy和Flask结合的问题也通常卡在维护 Session 生命周期上。选择一款框架绝对不是拿来主义,更多的是对原框架理念的一种赞同。
最后我还是考虑Mock掉这些API吧,哈哈。
Referencessubprocess – Work with additional processesget a lot knowledge of subprocess
singleton.py
The same question at StackOverflow
# 来源:HX
在微博上关注: 新浪, 腾讯 投稿
最新招聘
[北京] Python web开发程序员 – 译言 [北京] Python & Web前端研发工程师 – 北京邦富信息咨询有限公司 [广州] Python 开发工程师(Django) – 广州智品网络科技有限公司 [北京] Web前端开发程序员 – 译言 [上海] python工程师 – 上海客聚信息科技有限公司
更多>>
伟人所达到并保持着的高处,并不是一飞就到的,