如果访问量比较小,其实什么配置都没问题。在一些特殊场景下,例如促销活动,访问量会比较集中。峰值差不多能达到每秒1000到2000次的访问,而且还在继续增长中。
我们一直判断是数据库卡死的问题,后来发现,原来了服务器挂了。进一步定位,发现是apache接收了太多了请求,起了无数进程,内存用爆,机器卡死了。
如何配置apache,最大程度的发挥系统性能呢?
apache2三种模式
从2.0开始,apache引入了MPM(Multi-Processing Module,多进程处理模块)。MPM有prefork, worker和event(在2.4版本中稳定发布)模式,三种模式拥有不同的特点和性能。
prefork MPM
prefork是比较古老而又稳定的apache模式,特点是每个进程都是单线程,在一个时间点只能处理一个连接,需要启动大量的进程来处理高并发的请求。由于是单线程进程,因而无须考虑线程安全的问题,可以使用非线程安全的库,例如mod_php。
优点是成熟稳定,缺点是比较消耗内存,而且并发支持受限于进程数量,对高并发支持稍差。
worker MPM
worker同样使用多个进程,但每个进程又拥有多个线程,每个线程处理一个连接。由于线程是轻量级的,因而具有较高的并发性,同时,多个进程又获得了一定的稳定性。
worker模式特点是占用内存少,并发性比较高,缺点是必须考虑线程安全。如果使用了keep-alive方式,一个线程可能会被一直保持一个连接,但中间没有请求,直到超时。如果有多个线程被这样占据,在高并发场景下同样会出现无线程可用的情形。
event MPM
event模式是在2.4版本中才稳定发布的模式,它在worker的基础上,解决了keep-alive连接不能释放的问题。event MPM中,会有一个专门的线程来管理这些keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力。
apache2 配置
apache服务器的性能,在很大程度上取决于参数配置。
首先,我们如何查看当前apache所采用的模式呢?可以通过下面的命令:
apachectl -V | grep -i mpm
接下来,针对相应的模式,进行配置。apache2的默认配置文件位于/etc/apache2/apache2.conf,不同模式对应的配置文件在/etc/apache2/mods-available/下,有mpm_prefork.conf, mpm_worker.conf和mpm_event.conf。
prefork MPM
mpm_prefork.conf文件内容如下:
<IfModule mpm_prefork_module>
StartServers 10 # 启动时进程数
MinSpareServers 5 # 最小空闲进程数
MaxSpareServers 10 # 最大空闲进程数
MaxRequestWorkers 100 # 最大并发进程数
MaxConnectionsPerChild 10000 # 最大连接数限制
</IfModule>
各个指令的含义:
StartServers : apache2启动时创建的服务进程数量。
MinSpareServers:最小空闲进程数量,空闲进程指的是没有处理请求的进程。
MaxSpareServers : 最大空闲进程数量。
MaxRequestWorkers : 最大同时处理请求的进程数量,也是最大的同时连接数,表示了apache的最大请求并发能力,超过该数目后的请求,将排队。
MaxConnectionsPerChild : 进程生命周期内,处理的最大请求数目。达到该数目后,进程将死掉。如果设置为0,表示没有限制。该参数的意义在于,避免了可能存在的内存泄露带来的系统问题。
通过上面的介绍可以发现,prefork模式下,影响并发性能最重要的参数就是MaxRequestWorkers,它决定了apache的并发处理能力。但是这个参数不是越大越好,因为如果超出了系统硬件能力,机器会卡死。
如果确定合适的MaxRequestWorkers呢?
首先,通过top命令查看apache进程占用的资源,主要看%CPU和%MEM这两个指标,例如,每个进程的CPU占用率不超过1%,每个进程的内存占用率不超过2%,考虑内存限制,比较合适的apache进程数量为50个。
然后,逐步测试最大值。通过观测得来的CPU和内存的指标有一定的误差,一般可以适当调节这个数值,例如调到1.5或者2倍,再通过峰值场景下的机器是否卡顿来判断是继续上调还是下调。
调整完参数后,一般需要重启apache。
service apache2 restart
worker MPM
mpm_worker.conf文件内容如下:
<IfModule mpm_worker_module>
StartServers 2 # 启动时进程数
MinSpareThreads 25 # 最小空闲线程数
MaxSpareThreads 75 # 最大空闲线程数
ThreadLimit 64 # 每个进程可以启动的线程数量上限值
ThreadsPerChild 25 # 每个进程可以启动的线程数量
MaxRequestWorkers 400 # 线程数量最大值
MaxConnectionsPerChild 0 # 最大连接数限制
</IfModule>
配置文件和前面的prefork有一些变动,但是基本的概念差不多。原来以进程为单位的概念转变成了以线程为单位的概念,例如MinSpareServers转变为MinSpareThreads,多了一个ThreadsPerChild,表示每个进程可以有的线程数量,其上限值为ThreadLimit。
注意,修改了ThreadsPerChild后可以通过apache2 restart来生效,但是修改了ThreadLimit只能先stop,然后start。
在worker模式中,MaxRequestWorkers转变为了线程数量,比起prefork模式下可以有比较大的增长,通常可以有好几倍的差别。这是worker模式相比于prefork的优势,worker拥有比较多的处理线程,可以同时保持大量的连接,峰值应对能力比较强。尽管出于CPU或者数据库的原因,这些连接不能在第一时间内被处理,可能产生较大延迟。
如果确定worker模式的参数呢?
有两个参数比较重要是,一是ThreadsPerChild,这个表示每个进程的线程数目,一般采用默认值即可,如果是并发量比较大,可以考虑加大这个值。另外一个就是MaxRequestWorkers了,在workers模式下,任务处理的负载会分发给php-fpm来处理,所以apache主要承担了建立和保持连接的任务,并不消耗太多资源,因而在理论上,MaxRequestWorkers可以非常大。
但是从实用体验来看,如果MaxRequestWorkers远超系统的请求处理能力,会有大量的请求排队,因而产生比较大的延迟,这个时候的体验并不好。所以这个参数一开始可以稍大,例如可以设置为prefork模式下的几倍,然后根据线上峰值延迟不断调整这个数值。
MaxRequestWorkers必须是ThreadsPerChild的整数倍,否则apache会提示调整到一个相近的值。MaxRequestWorkers也有最大值限制,即ServerLimit乘以ThreadsPerChild。默认ServerLimit值是16,必要时可以显式声明加大这个值。
调整php-fpm
worker模式下的PHP一般运行在fastcgi环境下,所以决定系统性能的其实是PHP的处理能力。默认的php-fpm配置文件位于/etc/php5/fpm/pool.d/www.conf。通常有以下选项可以配置:
pm = dynamic # PHP进程管理方式,动态
pm.max_children = 32 # 最大PHP处理进程数
pm.start_servers = 2 # 启动时PHP进程数目
pm.min_spare_servers = 4 # 最小的空闲PHP进程数
pm.max_spare_servers = 8 # 最大的空闲PHP进程数
pm.max_requests = 5000 # 进程生命周期处理的最大请求数,避免内存泄露
同理,这里比较关键的是max_children参数,它直接决定了PHP处理请求的能力。这个参数的选取,也需要根据每个php-fpm所占用的CPU和内存来决定,同样通过top命令查看php-fpm进程消耗的资源,然后计算一个大概值。在调整的过程中,可以结合top命令查看CPU和内存消耗的总量,只要有足够剩余,是可以加大这个数值的。调整完参数后,需要重启php-fpm。
service php5-fpm restart
event MPM
event模式下的参数意义和worker模式完全一样,按照上面的策略来调整即可。
event相比于worker的优势是,它解决了worker模式下长连接线程的阻塞问题。
值得一提的是,worker/event模式的请求处理模式,已经和nginx的libevent模式相同了。在mod_php模式下,每个apache进程都需要直接执行php,所以很容易达到系统资源限制。但是在php-fpm模式下,apache只负责建立连接,然后把请求传递给php-fpm来处理。这样,apache可以保持大量的连接,请求处理能力取决于php服务器的性能。
由于event是明显优于worker的,所以在apache2.4及后续版本中,一般优先选择event模式。
常见问题
查看apache的error日志,可以发现许多系统运行中的问题。
server reached MaxRequestWorkers setting
[mpm_prefork:error] [pid 1134] AH00161: server reached MaxRequestWorkers setting, consider raising the MaxRequestWorkers setting
进程或者线程数目达到了MaxRequestWorkers,可以考虑增加这个值,当然,先考虑增加硬件。
scoreboard is full
[mpm_event:error] [pid 7555:tid 140058436118400] AH00485: scoreboard is full, not at MaxRequestWorkers
这个问题好像是apache2自带的bug,我们无力解决。好在这个问题一般只会影响单个线程,所以暂时可以忍。
一些系统状态指标
top命令
第一行里面的load average可以查看系统负载情况。第三行可以查看CPU使用问题。第四行可以查看内存占用及剩余内存。
进程列表里面,%CPU和%MEM显示了每个进程所占用的资源百分比。
apache日志
访问日志默认位置/var/log/apache2/access.log,可以通过日志文件查看每个请求处理结果及所用时间。
error日志一般位于/var/log/apache2/error.log,可以查看apache运行中的一些error报告 。
ps命令
查看当前系统apache和php进程、线程数目,下面的命令适用于ubuntu系统。
查看apache进程。
ps aux | grep apache
查看apache进程数目
加上wc -l 命令即可。
ps aux | grep apache | wc -l
worker/event模式下,查看线程数目。
ps -eLf | grep apache | wc -l
查看php-fpm进程数。
ps aux | grep php-fpm | wc -l
apache性能调试是一个比较复杂的问题。除了apache本身配置、PHP配置,还会涉及到数据库。后台的难点就在于如何应对高并发、大流量的请求,这往往不是单个点可以解决的,需要系统各个模块来协调。