问题描述

之前在搭建环境的时候出现了一个问题,搭建好了的LAMP环境,程序跑起来之后,总是会出现突然一下子500服务器错误,然后马上又好了的状况;就是间歇性Apache服务端500崩溃。

排查错误

首先我们可以排除的问题是程序出错,我跑的wordpress,一切是正常的,只是会突然出现这个情况,wordpress没有已知的这种bug,所以不会是程序问题。那么我们来看一下错误日志吧。

查找Apache的Log记录,出现得最多的记录是:PHP Fatal error: Out of memory (allocated 2097152) (tried to allocate 8192 bytes) in 。。。 on line 390

是内存不足导致的!我们来看一下这个服务器是不是连个网站都跑不起来了,这有点尴尬。首先我查看一下php.ini的内存配置:

这个数字没问题,这样配是可以的。

查看一下Apache的内存占用:

单个的httpd进程竟然有200多M,因为之前并没有研究过Apache的内存占用问题,在我看来这有点夸张了,一个小PHP程序不应该这样。于是再仔细查看一下问题所在。

使用top命令,然后shift+M命令看一下CPU和内存占用情况:

最高的那个进程,占掉了26.8%,看来已经找到了原因,就是Apache占用内存太高了,我的服务器只有1G内存。查了一下资料:

Apache中,影响apache性能的几个重要参数有:

  • KeepAlive 是否允许持续连接
  • MaxKeepAliveRequests 允许的持续连接的最大数
  • KeepAliveTimeout 持续连接在没有请求多少秒后切断
  • StartServers 最初启动时启动多少个服务器进程
  • MinSpareServers 空闲服务器进程的最小数
  • MaxSpareServers 空闲服务器进程的最大数
  • MaxClients 同时处理的请求数(最重要的参数,要少于ServerLimit)
  • MaxRequestsPerChild 每个子进程处理的最大请求数

网上的资料都说锅在MaxRequestsPerChild这个配置上,于是我尝试了一下修改这项配置:

<IfModule mpm_prefork_module>
MaxRequestsPerChild 0
</IfModule>

改为

<IfModule mpm_prefork_module>
MaxRequestsPerChild 500
</IfModule>

重新启动Apache服务,来看一下:

内存占用明显降低。但是过了一段时间之后我们再来看:

pache进程在使用内存时,是“渐长”的。也就是说,直到这个进程死掉,使用内存的数量是一直增长而不会减少的。

所以重点在于,什么时候杀死堆积到一定内存占用的Apache进程?

在上一张图的的最大内存占用的进程由26%到30%多的时候。当我再次刷新页面时,报500错误了!这时候我就想,是不是最上面的那个进程,被杀掉了,然后报了500错误。看一下占用,果然,最大的那个没了,在下面产生了一个新的:

所以,报500的原因其实是,进程突然被杀掉了!

MaxRequestsPerChild 参数详解
这个参数是说,apache进程在处理了多少个请求之后,必须退出,重新开始,以免在处理中的内存问题。
对于php脚本来说,把这个参数设置的小一些是有好处的,可以避免程序使用的内存持续增长对apache带来的压力:让这个参数定期释放内存,因为php是在脚本执行完毕后,自动释放只用的资源(内存)的。
比如设置为50?如果太小的话,重新产生一个apache进程也是要消耗资源的,这是一个平衡问题。
最好的设置方法是根据服务器内存情况设置一个合理的值。

那这样来看的话,MaxRequestsPerChild参数其实越大越好(如果服务器够强),甚至是原先的0(不限制),这样就不会无端端被杀掉进程;而服务器内存比较低的情况下,设置一个比较低的值是可以防止总内存值溢出;最好的做法是根据服务器情况设置一个合理的值。

很明显MaxRequestsPerChild并不是我们现在所需要的,那么这个问题该怎么解决呢?

接下来发现了 KeepAliveTimeout 这个参数:

KeepAliveTimeout 这个参数决定了,在什么都不做之前,一个http进程能够等待多长时间?设想一下,如果keepalive设置为on,而 keepalivetimeout设置为一个比较大的数字,apache占用内存会很快的增长。这是因为,一个apache进程完成了一个任务(并达到了一定的内存占用,想一下“渐进”模式),并不会马上退出,而是等待一个keepalivetimeout时间。假设用户的链接请求持续不断的到来,则积累起来的无用的apache进程就会相当多,直到timeout,这些进程才会被杀死。
但是,keepalive的确对于静态的文件,比如图像文件的传送是很有效的,因此,keepalive要设置为on,(off)但是keepalvietimeout要设置的小些。

这似乎正是我们所要的。根据这个描述,我再次修改了一下配置文件,这次情况好多了,虽然有所改善,但是还是会存在500错误的问题。

目前不知道是不是哪一步配置出了问题,还是服务器的内存的确太少,这个间歇性错误似乎无可避免,只能降低其频率。

我最终的解决方案,其实很简单,换Nginx,看一下nginx的表现:

php-fpm监控php-cgi的方式,非常高效,占用内存非常的少,而且很稳定!突然明白了为什么Nginx越来越受欢迎了。