gevent 为什么打了猴子补丁 还是会出现无限递归错误?

2020-03-12 17:34:10 +08:00
 Ritter

环境是 Flask + Gunicorn + gevent
已经在入口函数打了 monkey patch
可是在别的文件调用 requests 方法还是会报无限递归错误
文件层级结构

- manage.py
- gunicorn_config.py 
- function
-- handler.py
-- ...
-- packageA
---- api.py <- 此处调用 requests 方法 eg. get(), post()

gunicorn 配置

manage.py 开头已经打了 monkey patch

gunicorn 运行命令

gunicorn -c gunicorn_config.py manage.py

错误

版本 python3.6.9 gunicorn 20.0.4 gevent 1.4.0 docker images alpine (费了老半天才装上 gevent...)
先谢谢各位大佬

3780 次点击
所在节点    Python
19 条回复
Ritter
2020-03-12 17:53:31 +08:00
大佬们都下班了吗 T T
Ritter
2020-03-12 18:02:15 +08:00
gunicorn -c gunicorn_config.py manage.py 写错了
应该是 gunicorn -c gunicorn_config.py manage:app
cz5424
2020-03-12 19:59:28 +08:00
看不懂啥操作,猴子补丁跟你 requests 递归有什么关系?
wuwukai007
2020-03-12 20:57:32 +08:00
代码贴全一点
Qzier
2020-03-13 02:56:33 +08:00
把 flask 换成 starlette
Ritter
2020-03-13 08:53:45 +08:00
@cz5424 不打猴子补丁也会报这个错误 但是我看网上说 gunicorn 使用 gevent 内部已经 patch 了 很奇怪
Ritter
2020-03-13 08:54:40 +08:00
@wuwukai007 其他的没什么代码了就是在 api.py 文件调用了 requests 的 get,post 方法
Ritter
2020-03-13 08:58:51 +08:00
@Qzier 公司所有 Python app 都是用这个框架 这个不好换。。。
Ritter
2020-03-13 09:04:30 +08:00
github.com/gevent/gevent/issues/1531
这个 issues 跟我情况差不多
todd7zhang
2020-03-13 09:07:16 +08:00
todd7zhang
2020-03-13 09:08:31 +08:00
这里有说, 先 monkey.patch_all(), 你可以看看是不是 gunicorn_config.py 第一行的 from function.handler import app, db 这里面是不是有 import requests
Ritter
2020-03-13 09:22:06 +08:00
@todd7zhang 已经试过先在 gunicorn_config 打 patch 还是一样会报错
ClericPy
2020-03-13 09:27:17 +08:00
呃, 提几个常识

1. 猴子补丁要加在所有文件最顶端引用, 看你这情况 config.py 明显是在 manage.py 的顶端, 所以不该在后者打, 而是在前者那里打
2. gunicorn 借用 gevent 提速来优化 socket io 的时候, 连 workers 也要换的, 具体操作一般是 gunicorn -k gevent, 也就是你在 config.py 里的 worker class 那个参数, 所以就像第一点说的, 这个文件顶部才需要打, 不需要在 manage 里显性再 patch all 一次.

所以我一般 gunicorn handle flask 的时候, 是单独开一个符合 wsgi 的 py 文件, 里面产生一个 app, 而不是用 flask 自带生成的 manage.py 来操作

PS: 实际看你的报错, 最常见于违反相互引用的问题 (昨天刚写出来一个这种错误, 虽然大部分情况下 python 做过优化来防止多次引用, 但相互依赖还是违反常识的会递归报错), 比如 A 模块里的 a 在初始化的时候依赖 B 模块的 b 的初始化, 恰恰 B 在初始化的时候又依赖了 A 里 a 的初始化, 也就产生了死锁无限递归去初始化
Ritter
2020-03-13 09:33:43 +08:00
@ClericPy 已经试过在 config 前打了还是会报错 我看了 gunicorn 内部使用 gevent 的时候也已经 patch 了 所以我觉得应该不需要打了吧 可以顺便请教一下你是怎么单独开一个符合 wsgi 的 py 文件运行 gunicorn 的吗 谢谢
ClericPy
2020-03-13 09:50:39 +08:00
@Ritter #14 实际上就是搞一个空文件把创建 app 对象的过程单独抛出来, 网上到处都是...
```
manage 因为我六七年没用 flask 命令行去初始化也不知道里面经历的什么... 看你上面图片 config 开头就引用 app 就是个典型错误, 因为补丁要打在所有操作之前, 而 config 的第一个操作不像打补丁而是 去 handler 里 import 东西, 所以这个错误不知道你后面修了没有
```

我回复里点 py 居然报了一大堆 请不要在每一个回复中都包括外链,这看起来像是在 spamming

这论坛也不能贴代码...
ClericPy
2020-03-13 09:53:12 +08:00
在 docker 里调试如果费劲, 可以用最短案例先复现一个必定发生的代码吧, 那样举例子不会暴露源码也能更清晰地找问题...

又重新看了下你报错, 我好像误解了, 实际错误应该是 gevent 在 3.6 上给 ssl 打补丁的那个常见错误
Ritter
2020-03-13 11:24:42 +08:00
@ClericPy docker 不打 patch 都可以 线上就 bug 我吐了
ClericPy
2020-03-13 12:48:02 +08:00
@Ritter #17 是啊, 就是 patch 位置不对, 一定要在第一行要执行的代码里打, 用多了就习惯了, 三四年没碰过 gevent 了...
triangle111
2020-03-13 16:21:29 +08:00
import gevent.monkey
gevent.monkey.patch_all()

# 服务地址( adderes:port )
bind = '127.0.0.1:5002'
# 启动进程数量
workers = 2
worker_class = 'gevent'
threads = 20
preload_app = True
reload = True
x_forwarded_for_header = 'X_FORWARDED-FOR'
chdir = './'
proc_name='gunicorn.pid'
#记录 PID
pidfile='debug.log'
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
#设置 gunicorn 访问日志格式,错误日志无法设置
errorlog = "./errlog"
accesslog = "./logs"

贴一下自己

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

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

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

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

© 2021 V2EX