uwsgi托管Django的坑

发布时间 2023-04-13 14:56:43作者: 透明飞起来了

背景:

本人接受了一个python3 django框架写的系统,有api有页面。

部署方式是在win服务器上使用docker toolbox部署的,这是一种一起docker desktop还没萌芽或者刚刚萌芽时就存在的一种技术,简单说就是让人可以在win系统上使用docker,构建docker镜像,启动docker容器等待。

这次有一个开发需求:

用户A通过http形式调用我方的接口告诉我他出错了,但是他也有可能从错误中恢复过来,也有可能无法恢复。

如果恢复过来,就通过同一个接口使用不同参数告诉我他已经恢复了,我方系统就啥动作都不要做了。

如果一直没有恢复(也就是一直没有再通过这个接口将表示已经恢复的参数传递给我方系统),10秒钟后,我方系统就需要再告知第三方的一个系统B,让系统B知道用户A这家伙出错了,然后系统B会执行某种动作(具体是什么动作我方系统不需要知道)。我方通知系统B也是通过http形式。

 

以上就是全部背景了,按理说,这么个小需求应该不难实现。事实也是如此,我在本地没多少时间就开发完成了,使用的是`threading.Timer`方法,设定一个定时器,10秒后调用某个方法发起http请求到系统B,如果10秒内再次收到用户A的已恢复调用,则调用`threading.Timer`返回对象的`cancel()`方法取消这个定时器就好了。

本地调试都没有问题,一切正常。

部署过程简单,只是把我新写的python文件放到服务器的对应目录上,然后找到对应的docker容器,restart一下就好了。

部署到服务器上后,发现问题不小!!!

这个定时器指定的方法怎么弄都不执行,除非你再次请求这个接口,而且是在第二次请求这个API接口的同时执行!WTF!!这和我想的不一样啊!我的想法是只要请求了一次这个接口,然后就等着10秒后就可以自动执行指定的通知系统B的方法了,而且我在本地调试时所看到的现象也是如此。但是,服务器上这个现象就不是这样!它偏不!

来来回回回回来来的改代码,验证,怀疑是不是哪里报错了呀?是不是服务器的docker容器的linux环境和本地开发的win环境不一样导致的啊?是不是服务器docker toolbox这个已经废弃的技术有某种隐藏BUG啊?甚至写了一小段代码,模仿这个等待10秒后发起http请求的逻辑,用一个简单的python脚本承载。然后在服务器对应的那个docker容器内部通过python xxx.py方式运行这小段代码,实验结果也是和我的预期一样的,定时器正常工作了,10秒后http请求顺利发起了。

怀疑人生了。

想着是不是`threading.Timer`方法设置的定时器是不是有缺陷,但是网上找了很久也没看到有人说有缺陷的。

行吧,我且换一种实现试试,这次用`apscheduler.schedulers.background`模块的`BackgroundScheduler`,主要代码如下:

#实例化
timer = BackgroundScheduler()
timer.add_job(callSystemB, 'date',
                           run_date=datetime.datetime.now() + datetime.timedelta(seconds=10),
                           args=[data])
#启动 timer.start()
#不需要时(也就是用户A告知已经恢复时)就shutdown这个计划: timer.shutdown()

 

写完代码更新到服务器上去,重启docker容器。访问,还是有问题。

 

 但是这次问题清晰多了,很多人碰到过:https://www.cnblogs.com/zhuminghui/p/9252878.html,https://www.jianshu.com/p/78c46b0716b9

到这才知道是uwsgi托管django搞的鬼,uwsgi默认是one thread one processor,没有请求的时候,进程被挂起,子线程也就被挂起了。

在uwsgi.ini配置文件中添加了`enable-threads = true`后,定时器终于如我预期的工作了,问题解决。

然后再回头一想,是不是之前的`threading.Timer`也是由相同的问题造成无法执行的?再次验证后发现果然如此!!

可真坑!!