问题
最近几天使用Flask开发应用,开发环境启动正常,但是部署到线上环境就有问题,有一个请求第三方HTTP的接口报异常,具体异常如下:
RecursionError: maximum recursion depth exceeded while calling a Python object
从字面意思理解:应该是某个地方递归调用导致超出了最大递归深度,当然最大递归深度肯定不至于正常的业务受影响,检查了所有代码,根本没有出现递归调用。
网上搜索出来的内容,大概有这几种情况,但以下这些情况都解决不了我的问题:
一 python递归调用问题
Python的递归深度是有限制的,默认是1000,当递归超过1000此时就报错。
那对应的解决办法就是检查代码,如果确实循环调用需要递归超过1000次,就增大递归深度的参数,如下:
import sys
sys.setrecursionlimit(5000)
二 gunicorn使用gevent导致的bug
gunicorn以gevent方式启动,在gunicorn使用gevent时,系统会使用monkey patch。系统的部分函数会被修改,这个原因就是在python官方包ssl导入之后才进行patch,修改了ssl.py的SSLContext,导致ssl模块出现bug。原因详细分析可见这篇文章:https://www.cnblogs.com/buxizhizhoum/p/16264833.html
因此,在使用gevent,有些库要选择兼容gevent的版本。例如,任务调度的库apscheduler,web socket需要socketio的库等,需要专门选择gevent的函数。而有些库则直接无法使用,例如多进程multiprocess。(参见:https://blog.csdn.net/yyw794/article/details/104741340)
解决方案:在Python官方包ssl导入之前就执行gevent.monkey.patch_all()
但是对于这个时间点,网上大多数文章没有给出明确的说法,前面那篇文章的作者分析的比较透彻,给出的方案也比较靠谱,可以看看:
既然问题在于ssl包导入之后才进行patch,那么我们前置patch即可,考虑到配置文件加载在加载app之前,如果我们在配置文件加载时patch,则是目前能够找到的最早的patch时机。
配置文件gunicorn_config.py
import gevent.monkey
gevent.monkey.patch_all()
workers = 8
启动命令
gunicorn --config gunicorn_config.py --worker-class gevent --preload -b 0.0.0.0:5000 app:app
但其实,我的应用在app.py的create_app方法中首先加载了自定义的config.py,在我自定义的config.py里应用gevent.monkey.patch_all()也可以达到同样的效果,故我的启动脚本不用–config参数,实际上我是在docker-compose方式部署,完整启动脚本如下:
1 | gunicorn \ |
其实我在启动脚本中取消–preload也可以达到同样的效果,细究了一下preload的含义,
preload
--preload 选项会在所有 Worker分叉(forked)之前加载应用,这有助于减少内存使用,加快服务启动时间,但有时会导致不兼容问题。
官方的解释(原文链接:https://docs.gunicorn.org/en/latest/settings.html#preload-app):
1 | Command line: --preload |
也就是启用preload之后,gunicorn会预先(分叉)一些worker进程,提高服务器处理性能。
默认情况下,gunicorn的每个进程,会将代码重新加载一次,以保障进程之间是互相隔离的。这样可以做到更好的兼容性。
但是,有些情况,需要多个进程共享同一个资源时,或多个进程只能开启1个任务时,则需要使用–preload
使用preload后,API函数之外的初始化代码,只会出现在gunicorn的管理进程中,以共享的方式让worker进程访问
在检索这些的时候,发现很多有用的关于gunicorn知识,详细的可以看引用来源。
参考资料
gunicorn的实践经验:https://blog.csdn.net/yyw794/article/details/104741340
Gunicorn的预分叉架构:快速启动与高效资源利用:https://blog.csdn.net/2401_85639015/article/details/140335553
Gunicorn官网关于配置项:https://docs.gunicorn.org/en/latest/settings.html#preload-app
RecursionError: maximum recursion depth exceeded]:https://www.cnblogs.com/buxizhizhoum/p/16264833.html