Python Gunicorn、Django、Gevent:生成的线程正在阻塞
我们最近使用geventworker切换到了Gunicorn 在我们的网站上,我们有一些任务需要一段时间才能完成。超过30秒 序言 我们已经做了整个芹菜的事情,但是这些任务很少运行,所以让芹菜和redis一直运行是不可行的。我们只是不想那样。我们也不想按需启动芹菜和redis。我们想摆脱它。(对此我很抱歉,但我想避免这样的回答:“你为什么不用芹菜,它很棒!” 我们想要异步运行的任务 我说的是执行3000个SQL查询(插入)的任务,这些查询(插入)必须一个接一个地执行。这并不经常发生。我们还限制一次只运行其中的两个任务。它们应该需要2-3分钟 方法 现在,我们正在做的是利用gevent worker和Python Gunicorn、Django、Gevent:生成的线程正在阻塞,python,django,multithreading,gunicorn,gevent,Python,Django,Multithreading,Gunicorn,Gevent,我们最近使用geventworker切换到了Gunicorn 在我们的网站上,我们有一些任务需要一段时间才能完成。超过30秒 序言 我们已经做了整个芹菜的事情,但是这些任务很少运行,所以让芹菜和redis一直运行是不可行的。我们只是不想那样。我们也不想按需启动芹菜和redis。我们想摆脱它。(对此我很抱歉,但我想避免这样的回答:“你为什么不用芹菜,它很棒!” 我们想要异步运行的任务 我说的是执行3000个SQL查询(插入)的任务,这些查询(插入)必须一个接一个地执行。这并不经常发生。我们还限制一
gevent.spawn
任务并返回响应
问题
我发现生成的线程实际上是阻塞的。一旦返回响应,任务就开始运行,在任务停止运行之前,不会处理任何其他请求。任务将在30秒后终止,gunicorn超时。
为了防止这种情况,我在每次SQL查询之后都使用time.sleep()
,这样服务器就有机会响应请求,但我觉得这不是重点
设置
我们运行gunicorn、django并使用gevent。所描述的行为发生在my dev环境中,并使用1个gevent worker。在生产中,我们也将只运行1名工人(目前)。此外,在任务阻塞时,运行2个worker似乎无助于服务更多的请求
太长,读不下去了
我们认为在2分钟的任务中使用G事件线程是可行的。
(放在芹菜上)
- 我们将gunicorn与gevent一起使用,不知道为什么要使用线程
使用gevent生成。生成被阻止
- 是有意阻止还是我们的设置错误
谢谢大家! 在后台运行任务的一种方法是fork
父进程。与Gevent不同,它不会阻塞--您正在运行两个完全独立的进程。这比启动另一个(非常便宜的)绿地要慢,但在这种情况下,这是一个很好的权衡
您的流程分为两部分,父级和子级。在父级中,返回对Gunicorn的响应,就像在普通代码中一样
在子对象中,执行长时间运行的处理。最后,通过执行专门版本的exit
进行清理。下面是一些处理和发送电子邮件的代码:
if os.fork():
return JsonResponse({}) # async parent: return HTTP 200
# child: do stuff, exit quietly
ret = do_tag_notify(
event, emails=emails, photo_names=photo_names,
)
logging.info('do_tag_notify/async result={0}'.format(ret))
os._exit(0) # pylint: disable=W0212
logging.error("async child didn't _exit correctly") # never happens
小心这个。如果在子对象中抛出异常,即使是语法错误或未使用的变量,您也永远不会知道!带有日志记录的父级已不存在。日志记录要详细,不要做太多
使用fork
是一个很有用的工具——玩得开心 我已经决定使用一个同步
(标准)工人,并使用多处理
库。这似乎是目前最简单的解决方案
我还实现了一个全局池,滥用memcached
缓存提供锁,因此只能运行两个任务。这里似乎没有人对您的问题给出实际答案
是有意阻止还是我们的设置错误
您的设置有问题。SQL查询几乎完全是I/O绑定的,不应该阻止任何Greenlet。您使用的SQL/ORM库对gevent不友好,或者代码中的其他内容导致了阻塞。这种任务不需要使用多处理
除非您在greenlet上显式地执行join
,否则服务器响应不应该被阻塞。只是在greenlet中执行代码。实际上,您必须调用异步API才能使greenlet不阻塞。例如,您正在拨打的time.sleep
电话仍会在绿色小房间内阻塞。您应该使用gevent.sleep
执行非阻塞睡眠。您的数据库调用可能也被阻塞了,除非您使用的是gevent monkey修补程序。@dano因为我正在与gevent worker一起使用gunicorn,所以monkey修补程序由我负责<代码>时间。睡眠
允许线程更改,因此它是非阻塞的。也许是猴子补的。但是,我希望把任务交给一个工人,然后由他来处理。因此,只要它并行运行,它就可以阻止它想要的一切。但我猜Greenlet不能并行运行?Greenlet可以并发运行,但它们仍然是单线程的;一次只能使用其中一个CPU。因此,如果一个greenlet处于阻塞I/O调用或gevent.sleep
,另一个greenlet可以运行。但是,如果一个greenlet正在处理数字或解析XML(或任何其他基于CPU的操作),那么其他greenlet将不会运行。如果要执行非异步的I/O操作,greenlet也会阻止其他greenlet,这意味着它不是由gevent修补的monkey,也不是插入gevent的事件循环。你说你在做插入,但后来说你在使用django ORMs。因此,您可能要做的不仅仅是插入(否则您可以在一个sql调用中完成3000次插入)。因为它使用的是CPU,所以会阻塞,就这么简单。停止抱怨不想经营芹菜和redis。Redis是轻量级的,您可以在0到1之间自动缩放芹菜。很有可能你会使用redis做更多的事情。@dalore,有时你不是这些决定的主人,你不得不做一些其他的事情。另外,没有人说我会使用Django ORM。我下面的解决方案已经运行了好几个月了,但是感谢您的时间尽管我觉得这不起作用,但我尝试了,它确实不起作用。当执行的代码