如何防止WSGI导致Apache挂起

9 投票
2 回答
10924 浏览
提问于 2025-04-15 13:21

我在用WSGI运行Django,代码是这样的:

<VirtualHost *:80>
    WSGIScriptAlias / /home/ptarjan/django/django.wsgi
    WSGIDaemonProcess ptarjan processes=2 threads=15 display-name=%{GROUP}
    WSGIProcessGroup ptarjan
    Alias /media /home/ptarjan/django/mysite/media/
</VirtualHost>

但是如果我在Python中这样做:

def handler(request) :
    data = urllib2.urlopen("http://example.com/really/unresponsive/url").read()

整个Apache服务器就会卡住,变得没有反应,出现了这个错误追踪:

#0  0x00007ffe3602a570 in __read_nocancel () from /lib/libpthread.so.0
#1  0x00007ffe36251d1c in apr_file_read () from /usr/lib/libapr-1.so.0
#2  0x00007ffe364778b5 in ?? () from /usr/lib/libaprutil-1.so.0
#3  0x0000000000440ec2 in ?? ()
#4  0x00000000004412ae in ap_scan_script_header_err_core ()
#5  0x00007ffe2a2fe512 in ?? () from /usr/lib/apache2/modules/mod_wsgi.so
#6  0x00007ffe2a2f9bdd in ?? () from /usr/lib/apache2/modules/mod_wsgi.so
#7  0x000000000043b623 in ap_run_handler ()
#8  0x000000000043eb4f in ap_invoke_handler ()
#9  0x000000000044bbd8 in ap_process_request ()
#10 0x0000000000448cd8 in ?? ()
#11 0x0000000000442a13 in ap_run_process_connection ()
#12 0x000000000045017d in ?? ()
#13 0x00000000004504d4 in ?? ()
#14 0x00000000004510f6 in ap_mpm_run ()
#15 0x0000000000428425 in main ()

我是在Debian的Apache 2.2.11-7上遇到的。

类似地,我们能否防止这种情况发生:

def handler(request) :
    while (1) :
        pass

在PHP中,我会设置时间和内存限制。

2 个回答

3

如果我理解得没错,你的问题是想保护Apache服务器,避免在运行一些不可靠的脚本时出现卡死的情况。其实,如果你在运行不可信的代码,我觉得你应该担心的事情比Apache卡死更严重。

不过,确实可以通过一些配置来创建一个更“安全”的环境。下面这两个配置非常有用:

  • WSGIApplicationGroup - 这个设置决定了WSGI应用属于哪个应用组。它可以让每个用户有不同的设置——同一个应用组里的所有WSGI应用都会在处理请求的进程的同一个Python子解释器中运行。

  • WSGIDaemonProcess - 这个配置用来为运行应用程序设置一个独立的守护进程。守护进程可以用与Apache子进程不同的用户来运行。这个配置有很多有用的选项,下面列出一些:

    • user=name | user=#uid, group=name | group=#gid:

      定义守护进程应该以哪个UNIX用户和组名(或者用户的数字ID和组的数字ID)来运行。

    • stack-size=nnn

      为每个由mod_wsgi在守护进程中创建的线程分配的虚拟内存大小(以字节为单位)。

    • deadlock-timeout=sss

      定义在检测到Python全局解释器锁(GIL)可能死锁后,守护进程被关闭并重启之前允许经过的最大秒数。默认是300秒。

你可以在这里了解更多关于配置指令的信息。

13

你不需要使用“死锁超时”('deadlock-timeout'),这个是为一些特殊情况设计的,跟你现在的问题没什么帮助。

如果你想用mod_wsgi的功能,应该使用“非活动超时”('inactivity-timeout')这个选项,适用于WSGIDaemonProcess指令。

不过,即使这样也不是一个完整的解决方案。因为“非活动超时”选项是用来检测守护进程是否停止处理所有请求的,并不是针对每个请求的超时。只有在守护进程是单线程的情况下,这个选项才能算作每个请求的超时。此外,如果在这个时间段内没有任何请求到达,这个选项还会导致守护进程重启。

总之,在mod_wsgi层面上没有办法实现每个请求的超时,这是因为在Python中没有真正的方法来中断一个请求或线程。

你真正需要做的是在你的应用代码中实现HTTP请求的超时。我不太确定这个功能现在是否已经实现,但你可以在网上搜索“urllib2 socket timeout”。

撰写回答