关于python:子进程阻止Django视图

subprocess blocks Django view

我从一个视图调用subprocess.Popen时遇到问题:
在子流程完成之前,不会显示调用subprocess.Popen的视图。
服务器立即发送"200 OK",但不发送页面内容。

我的问题是:这是Django开发服务器的限制还是我做错了?

服务器不会完全挂起,因为其他视图可以同时处理。

关于该主题已经有一些问题,谷歌提供了一些其他线索,但我无法找到我的问题的明确答案。

我相信这不是python问题,因为这个命令会立即终止:

python -c 'import subprocess; print subprocess.Popen(["/bin/sleep","10"]).pid'

如何重现

创建测试项目和应用程序:

cd /tmp
django-admin.py startproject django_test
cd django_test
./manage.py startapp subprocess_test

将urls.py和subprocess_test / views.py替换为:

  • urls.py:

    来自django.conf.urls.defaults import *

    urlpatterns = patterns('',
    (r'^ hello $','subprocess_test.views.hello'),
    (r'^ start $','subprocess_test.views.start'),
    )

  • subprocess_test / views.py

    来自django.http导入HttpResponse

    导入子流程

    def hello(请求):
    返回HttpResponse('Hello world!')

    def start(请求):
    subprocess.Popen(["/ bin / sleep","10"])
    返回HttpResponse('开始完成')

测试一下:

./manage.py runserver 0.0.0.0:8000

转到http://127.0.0.1:8000/hello和http://127.0.0.1:8000/start

测试结果

"start"需要10秒才能加载,并且"hello"可以在此期间加载。
例如,我得到这样一个日志:

[01/Feb/2011 07:20:57]"GET /hello HTTP/1.1" 200 12
[01/Feb/2011 07:21:01]"GET /start HTTP/1.1" 200 10
[01/Feb/2011 07:21:01]"GET /hello HTTP/1.1" 200 12
[01/Feb/2011 07:21:02]"GET /hello HTTP/1.1" 200 12

使用wget:

wget http://127.0.0.1:8000/start
--2011-02-01 14:31:11-- http://127.0.0.1:8000/start
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified
Saving to: `start'

1
[           <=>                           ] 10          --.-K/s   in 9,5s

2011-02-01 14:31:21 (1,05 B/s) - ? start ? saved [10]


看起来你不关心系统调用的结果是什么,所以我假设你正在尝试进行某种离线(或后台)处理。

我会建议一种更简洁的方式,而不是直接执行程序。使用诸如Gearman之类的排队系统来排队处理任务,然后让一个单独的工作人员使用队列中的项目。

这样做的好处是可以在发生大量流量峰值时保护您的服务器,因此每次向该视图发出请求时,您都不会分叉进程。您可以根据您的决定以慢速或快速的方式使用项目,而与流量无关。

交通可能不是问题,但我个人认为这也是一个更清洁的设计决策。


我在使用nginx + uwsgi运行Django时遇到了同样的问题。通常,断点是web服务器的模块,在我的例子中它是uwsgi。添加解决了这个问题。从另一方面来看,fastcgi模块不会导致查看挂起后台进程。

我不知道您使用哪个服务器,因此您可以使用各种模块(uwsgi,mod_wsgi,fastcgi)检查此行为,并查看哪种更适合您。
并尝试在后台执行:

1
subprocess.Popen(["/bin/sleep","10","&"])


试试这个

1
2
3
4
import subprocess

x = subprocess.Popen(["/bin/sleep","10"])
x.wait()