Django/Apache 使用 mod_wsgi 冻结

Posted

技术标签:

【中文标题】Django/Apache 使用 mod_wsgi 冻结【英文标题】:Django/Apache freezing with mod_wsgi 【发布时间】:2011-11-23 20:09:50 【问题描述】:

我有一个 Django 应用程序在 nginx 后面的 2 个负载平衡 mod_wsgi/Apache 服务器后面运行(静态文件、反向代理/负载平衡)。

每隔几天,我的网站就会完全没有响应。我的猜测是,一堆客户端正在请求被阻止的 URL。

这是我的配置

WSGIDaemonProcess web1 user=web1 group=web1 processes=8 threads=15 maximum-requests=500 python-path=/home/web1/django_env/lib/python2.6/site-packages display-name=%GROUP
WSGIProcessGroup web1
WSGIScriptAlias / /home/web1/django/wsgi/wsgi_handler.py

我尝试过仅使用单个线程和更多进程,以及更多线程和单个进程。我尝试的几乎所有事情迟早都会导致页面加载超时。

对我可以尝试什么有什么建议吗?如果可以解决问题,我愿意尝试其他部署选项。

另外,除了 Apache 状态模块之外,还有更好的方法来监控 mod_wsgi 吗?我一直在打:

 curl http://localhost:8080/server-status?auto

观察忙碌的工作人员的数量作为我是否即将陷入麻烦的指标(我假设我拥有的工作人员越多,当前发生的阻塞操作就越多)。

注意:其中一些请求是针对我为应用程序托管的 REST Web 服务的。以某种方式通过 Nginx 限制该 URL 位置是否有意义?

【问题讨论】:

你没有使用DEBUG=True 对吗? 【参考方案1】:

用途:

http://code.google.com/p/modwsgi/wiki/DebuggingTechniques#Extracting_Python_Stack_Traces

嵌入功能,您可以在您期望卡住的请求时触发并找出它们在做什么。请求可能会随着时间的推移而累积,而不是一次全部发生,因此您可以定期执行此操作,而不是等待完全失败。

作为故障保险,您可以添加选项:

inactivity-timeout=600

到 WSGIDaemonProcess 指令。

如果守护模式进程处于非活动状态 10 分钟,它将重新启动它。

不幸的是,目前这种情况发生在两种情况下。

第一种是10分钟内完全没有请求的情况下,会重新启动进程。

第二个,也是你想要启动的一个,如果所有请求线程都被阻塞并且它们都没有从 wsgi.input 读取任何输入,也没有产生任何响应内容,在 10 分钟内,该进程将再次自动重启。

这至少意味着您的进程应该会自动恢复,并且您不会被叫下床。因为您正在运行如此多的进程,所以它们可能不会同时卡住,因此新请求不应注意到重启,因为其他进程仍会处理这些请求。

您应该解决的问题是您可以将超时设置到多低。您不希望它太低以至于进程将因为根本没有请求而重新启动,因为如果使用延迟加载,它将卸载应用程序和下一个请求将导致速度变慢。

我应该做的实际上是添加一个新选项blocked-timeout,它专门检查在定义的时间段内被阻止的所有请求,因此将它与由于根本没有请求而重新启动分开。这将使其更加灵活,因为由于没有请求而重新启动它会带来再次加载应用程序的问题。

不幸的是,由于托管配置可能是多线程的,因此无法轻松实现适用于单个请求的请求超时。将 Python 异常注入请求不一定会解除线程阻塞,最终您无论如何都必须终止进程并中断其他并发请求。因此阻塞超时可能更好。

另一件有趣的事情可能是我在 mod_wsgi 中添加一些东西来报告由于阻塞进程而导致的强制重启到 New Relic 代理中。那真的很酷,因为您可以在监控工具中看到它们。 :-)

【讨论】:

非常感谢分享!这帮助我在我们的一台服务器上发现了一个非常糟糕的问题。【参考方案2】:

我们在工作中遇到了类似的问题。我们能弄清楚的最好的事情是应用程序的竞争/死锁问题,导致 mod_wsgi 卡住了。通常杀死一个或多个 mod_wsgi 进程会暂时解除它。

最好的解决方案是转向全进程、无线程。我们与我们的开发团队确认,他们引入的一些 Python 库可能不是线程安全的。

试试:

WSGIDaemonProcess web1 user=web1 group=web1 processes=16 threads=1 maximum-requests=500 python-path=/home/web1/django_env/lib/python2.6/site-packages display-name=%GROUP

缺点是,进程比线程消耗更多的内存。因此,我们通常最终会得到更少的总体工人(因此 16x1 而不是 8x15)。而且由于 mod_wsgi 几乎没有提供关于工人有多忙的报告,所以除了盲目地调整你有多少工人之外,你就是 SOL。

好处是,这个问题再也不会发生了,应用程序又完全可靠了。

php 一样,除非您确定它是安全的,否则不要使用线程实现……这意味着核心(通常没问题)、框架、您自己的代码以及您导入的任何其他内容。 :)

【讨论】:

【参考方案3】:

如果我正确理解了您的问题,您可以尝试以下选项:

将 URL 提取移出请求/响应周期(使用例如 celery); 增加线程数(它们可以比进程更好地处理这些块,因为它们消耗的内存更少); 减少 urllib2.urlopen 的超时时间; 尝试 gevent 或 eventlet(它们会神奇地解决您的问题,但可能会引入另一个微妙的问题)

我认为这不是部署问题,这更多是代码问题,没有 apache 配置解决它。

【讨论】:

我没有在我的请求/响应中进行任何 URL 获取。我提到的 REST API 只是产生 JSON 输出的几个视图,但比人工请求更频繁地受到攻击。我想他们可能会导致某种堆积......

以上是关于Django/Apache 使用 mod_wsgi 冻结的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Django/Apache/mod_wsgi/MySQL 最大化 EC2 的性能?

Django,apache,mod_wsgi - 错误:脚本头过早结束

使用 WSGI 和 apache 设置 django

'使用mod_wsgi在apache上部署django app时,没有名为'encodings''的模块

Django / Apache / mod_wsgi - WEB 应用程序挂起 - 无法启动

Windows下部署Django(Apache+mod_wsgi)