我有一个 apache 网络服务器,我使用 mod_wsgi 使用烧瓶设置了一个网站。我有几个可能相关也可能不相关的问题。
每次调用某个页面(该页面运行一个执行需要超过 2 秒的繁重计算的函数),内存就会增加大约 20 兆字节。我的服务器开始时机器上的所有东西都消耗了大约 350 兆字节。该服务器总共有 3,620 兆字节显示在 htop 中。在我多次重新加载此页面后,服务器使用的总内存最终开始达到 2,400 兆字节左右,并且停止增加。在它达到这个级别后,我无法让它消耗足够的内存来在数百个页面重新加载后进入交换。这是烧瓶或apache或python的设计吗?对我来说,如果有某种缓存机制,如果每次调用同一个 URL,似乎不会发生内存累积。如果我重新启动 apache,内存就会被释放。
有时对该页面的调用会导致被调用函数出错,即使它们都是只读调用(不向磁盘写入任何数据)并且每个页面的查询字符串都是相同的。
我有另一个页面(调用另一个计算量少得多的函数),当与 Web 服务器上运行的其他页面同时调用时,随机出错或结果(图像)意外返回。
问题 2 和 3 是否与问题 1 相关?问题 2 和 3 可能是由于编程错误或机器内存不足造成的吗?我可以通过在大约 40 个 Firefox 选项卡中加载相同的 URL,然后选择“重新加载所有选项卡”选项来重现随机性。
应该提供哪些更多信息才能获得更好的答案?
我试过放置
import gc
gc.collect()
进入我的代码。
我有
WSGIDaemonProcess website user=www-data group=www-data processes=2 threads=2 home=/web/website
WSGIScriptAlias / /web/website/website.wsgi
<Directory /web/website>
WSGIProcessGroup website
WSGIScriptReloading On
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
在我的 /etc/apache2/sites-available/default 文件中。如果总共只创建了 4 个线程,那么内存似乎不会增长那么多,不是吗?
更新
如果我设置 processes=1 threads=4,那么当两个请求同时发出时,看似随机的问题就会一直出现。一个我设置processes=4 threads=1,那么看似随机的问题就不会发生了。尽管如此,内存的增加仍在发生,实际上现在将一直上升到系统的最大 RAM 并开始交换。
更新
虽然我还没有解决这个失控的 RAM 消耗问题,但我当前的应用程序几个月都没有遇到问题。显然它不是太流行,几天左右后,apache可能已经自动清除了RAM或其他东西。
现在,我做了另一个应用程序,它与前一个应用程序完全无关。之前的应用程序使用 matplotlib 生成大约 1 兆像素的图像。我的新应用程序正在使用 matplotlib 生成 20 兆像素图像和 1 兆像素图像。现在,当使用新应用程序生成 20 兆像素的图像时,问题变得更加严重。在整个交换空间被填满后,似乎有些东西被杀死了,并且在有一些可用的 RAM 和交换空间可用的情况下,事情以不错的速度运行了一段时间,但是当 RAM 被消耗时运行速度要慢得多。这是正在运行的进程。我不认为有任何额外的僵尸进程正在运行。
$ ps -ef|grep apache
root 3753 1 0 03:45 ? 00:00:02 /usr/sbin/apache2 -k start
www-data 3756 3753 0 03:45 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 3759 3753 0 03:45 ? 00:02:06 /usr/sbin/apache2 -k start
www-data 3762 3753 0 03:45 ? 00:00:01 /usr/sbin/apache2 -k start
www-data 3763 3753 0 03:45 ? 00:00:01 /usr/sbin/apache2 -k start
test 4644 4591 0 12:27 pts/1 00:00:00 tail -f /var/log/apache2/access.log
www-data 4894 3753 0 21:34 ? 00:00:37 /usr/sbin/apache2 -k start
www-data 4917 3753 2 22:33 ? 00:00:36 /usr/sbin/apache2 -k start
www-data 4980 3753 1 22:46 ? 00:00:12 /usr/sbin/apache2 -k start
虽然当我查看 htop 时我有点困惑,因为它显示的进程比 top 或 ps 多得多。
更新
我发现内存泄漏是由于matplotlib(或我使用它的方式),而不是flask或apache,所以我最初发布的问题2和3确实是与问题1不同的问题。下面是一个基本的我在 ipython 中以交互方式消除/重现问题的函数。
def BigComputation():
import cStringIO
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
#larger figure size causes more RAM to be used when savefig is run.
#this function also uses some RAM that is never released automatically
#if plt.close('all') is never run, but it is a small amount,
#so it is hard to tell unless run BigComputation thousands of times.
TheFigure=plt.figure(figsize=(250,8))
file_output = cStringIO.StringIO()
#causes lots of RAM to be used, and never released automatically
TheFigure.savefig(file_output)
#releases all the RAM that is never released automatically
plt.close('all')
return None
摆脱 RAM 泄漏的诀窍是运行
plt.close('all')
在 BigComputation() 中,否则,BigComputation() 将在每次调用函数时不断累积 RAM。我不知道我是否只是不恰当地使用了 matplotlib 或者编码技术不好,但我真的认为一旦 BigComputation() 返回,它应该释放除任何全局对象或它返回的对象之外的所有内存。在我看来,matplotlib 必须以不适当的方式创建一些全局变量,因为我不知道它们的名称。
我想我现在的问题是为什么我需要 plt.close('all')?我还需要尝试 Graham Dumpleton 的建议,以进一步诊断我的 apache 配置,看看为什么我需要在 apache 中设置 threads=1 以消除随机错误。