为什么在Django视图中使用Popen会挂起?

4 投票
1 回答
628 浏览
提问于 2025-04-18 14:55

在我的一个Django视图中,我需要启动一个异步任务,这个任务可能需要几分钟才能完成。因为使用Celery加消息队列对这个任务来说有点过于复杂,所以我用了subprocess.Popen来创建一个子进程,像这样:

args = ['python', SCRIPT_PATH, "&"];
try:
    subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Async call
except:
    pass
return HttpResponse("Job submitted")

我用nginx和gunicorn来运行我的Django应用。当我启动服务器并保持命令行会话打开时,上面的代码运行得很好。但是当我关闭命令行会话时,代码就停止工作,并且在Popen那一行卡住了。

我猜这和stdout(标准输出)和stderr(标准错误)参数有关,但我不知道该怎么改才能让它正常工作。

=========编辑===========

在我的脚本中,我使用了selenium webdriver和phantomjs来加载一个网页并提交一个表单。在这个过程中,selenium或phantomjs会向标准输出或标准错误写入信息。当我在命令行中启动gunicorn,然后关闭命令行会话时,selenium或phantomjs的写入就失败了。当我用下面这种方式启动gunicorn,并把输出重定向到一个文件时:

gunicorn app.wsgi > /tmp/gunicorn.log &

问题就解决了。

1 个回答

3

虽然我不能确定你提到的Popen那一行为什么会阻塞,但我可以给你一个替代方案。另外,我也不太确定你是否需要使用"&"这个选项。

你可以像这样使用NamedTemporaryFiles来处理标准输出和错误输出流:

from tempfile import NamedTemporaryFile
output = NamedTemporaryFile(delete=False)
error = NamedTemporaryFile(delete=False)
Popen(args, stdout=output, stderr=error) 

这样做不会造成阻塞。我想你应该已经处理好了Popen完成后要做的事情。

Popen的文档中有关于PIPE对象的警告。我把它贴在这里以便完整:

读取的数据会缓存在内存中,所以如果数据量很大或者没有限制,就不要使用这种方法。

撰写回答