子进程阻塞Django视图

3 投票
3 回答
3635 浏览
提问于 2025-04-16 11:00

我在调用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:

    from django.conf.urls.defaults import *

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

  • subprocess_test/views.py

    from django.http import HttpResponse

    import subprocess

    def hello(request):
      return HttpResponse('Hello world!')

    def start(request):
      subprocess.Popen(["/bin/sleep", "10"])
      return HttpResponse('start done')

测试一下:

./manage.py runserver 0.0.0.0:8000

访问 http://127.0.0.1:8000/hellohttp://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
正在连接到127.0.0.1:8000... 已连接。
HTTP请求已发送,等待响应... 200 OK
长度:未指定 [text/html]
正在保存到: `start'

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

2011-02-01 14:31:21 (1,05 B/s) - « start » 已保存 [10]

3 个回答

-1

试试这个

import subprocess

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

我在用Django配合nginx和uwsgi的时候也遇到了同样的问题。通常,问题出在网络服务器的模块上,在我这儿是uwsgi模块。添加了<close-on-exec/>后,这个问题就解决了。另一方面,fastcgi模块不会导致视图在后台进程中卡住。

我不知道你用的是哪个服务器,所以你可以试着用不同的模块(比如uwsgi、mod_wsgi、fastcgi)来检查一下这个问题,看看哪个更适合你。
另外,试着在后台执行:

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

看起来你并不在乎系统调用的结果,所以我猜你是在做一些离线(或者后台)处理。

我建议你用一种更干净的方法来处理,而不是直接执行一个程序。可以使用像Gearman这样的队列系统,把处理任务放到队列里,然后让一个单独的工作程序去处理这些队列里的任务。

这样做的好处是,当流量突然增加时,可以保护你的服务器,避免每次请求这个视图时都去启动一个新进程。你可以根据需要,慢慢或者快速地处理队列里的任务,跟流量无关。

虽然流量可能不是问题,但我个人觉得这样设计更干净。

撰写回答