php-fpm介绍,配置和优化


上个月把博客站从php5.5.7升级到了php7.3.4,一个月的时间已经发生多次服务器卡死的情况,之前顾不上深究,直接重启了事。今天抽点时间处理了下,顺便把相关的知识点记录下来。

一、php-fpm 介绍

虽然对php不熟,但是本着虽不求甚解,但不能不了解的思想,所以我还是去阅读了一些php-fpm的相关知识。php-fpm是php-fastcgi process manager的简称。所以了解php-fpm不得不稍微了解下fastcgi以及cgi的相关知识。下面是一些笔者觉得不错的参考文章:

其实看了许多文章,但是对比理解发觉,许多文章描述有误或语焉不详,比如对于nginx的fastcgi和php的fastcgi的关系,这里我稍微总结梳理下:

  • cgi: 对接标准输入和标准输出的程序,其实现与语言无关。HTTP服务器每次收到请求后,启动一个CGI进程并将请求传入,CGI进程完成后退出。这种fork-and-execute模式导致性能低下。
  • php-cgi: 包含php解释器的CGI程序(不是用php写的CGI程序),能够处理php请求。
  • fastcgi:一种用于更好的提供CGI功能的协议;fastcgi协议采用C/S结构,http服务器只需要包含fastcgi的C端模块,通过socket与支持fastcgi S端的CGI程序通信。这样既解决了CGI反复加载的问题,又可以将CGI程序和http服务器分离。nginx的fastcgi模块即fastcgi的C端。
  • php-fpm: 包含php解释器,实现了fastcgi的S端,并且提供了进程管理功能的程序。可以常驻内存并持续接受和处理php请求,并且能够控制进程的数量和生存周期。

二、php-fpm 配置和优化

php-fpm一直存在一些固有问题,如内存泄漏,进程僵死等;这些问题会导致占用内存一直增加,进程数不停增加,直到服务器资源耗尽死机。所以php-fpm的配置主要是围绕控制进程数和进程生命周期进行的(用这种方法来解决性能问题不得不说php团队也是对这个语言的没辙了)。

php中主要有这么两个配置文件:php.ini, php-fpm.conf; 前者是php解释器的配置,后者是php-fpm的配置。php的性能优化主要是调整进程管理相关参数即php-fpm.conf。

这里列举一些重要的配置,并做一些说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
error_log = /usr/local/var/log/php-fpm.log  #错误日志

emergency_restart_threshold = 60
emergency_restart_interval = 60s
#表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项一般保持默认值。0 表示 '关闭该功能'. 默认值: 0 (关闭).

process_control_timeout = 0
#设置子进程接受主进程复用信号的超时时间. 超过时间没有接受到信号则子进程优雅退出。可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0.

pm = dynamic
#php-fpm进程启动模式,pm可以设置为static和dynamic和ondemand
#如果选择static,则进程数就数固定的,由pm.max_children指定固定的子进程数。
#如果选择dynamic,则进程数是动态变化的,由以下参数决定:
pm.max_children = 50 #子进程最大数
pm.start_servers = 2 #启动时的进程数,默认值为: (min_spare_servers + max_spare_servers)/2
pm.min_spare_servers = 1 #保证空闲进程数最小值,如果空闲进程小于此值,则创建新的子进程
pm.max_spare_servers = 3 #保证空闲进程数最大值,如果空闲进程大于此值,此进行清理
pm.max_requests = 500
#设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 '0' 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0.

slowlog = log/$pool.log.slow
#慢请求的记录日志,配合request_slowlog_timeout使用,默认关闭
request_slowlog_timeout = 10s
#当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 '0' 表示 'Off'

额外说明:

  1. emergency_restart功能,process_control_timeoutslowlog功能默认都是关闭的,若有频繁假死,进程过多,死机等情况,建议都打开。

  2. 进程的数量要根据服务器的实际情况调整,不然也会耗尽资源。比如,每个php-fpm占用内存一般在30M到50M之间,对于1G内存的服务器,可能不到30个就会耗尽内存,所以可以设置:

1
2
3
4
5
6
pm = dynamic
pm.max_children = 30
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500 //单个进程不宜过大,防止内存泄漏

其实也不确定这样是否能高枕无忧,观察一段时间再看。对于解决不了的,网上还有一种终极大法:利用linux定时器任务执行脚本(或者后台持续运行脚本),在脚本中监测php-fpm的进程数量或系统内存占用,达到条件重启php-fpm,简单有效。

一个根据memory重启的例子:

checkmem.sh

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

while true;
do
# pcount = $(top -b -n1 | grep -c "php-fpm") 这个是统计进程数的
mem=$(top -b -n1 | grep "php-fpm" | awk '{sum=sum+$10};END{print sum}')
if [[ $mem > 70 ]];then
service php-fpm restart
fi
sleep 30
done

后台启动:

1
2
3
4
5
nohup ./checkmem.sh > checkmem.log 2>&1 &

# nohup 是不挂断的运行(忽略所有挂断(SIGHUP)信号)
# 最后一个&表示后台运行
# 2>&1 表示将标准错误重定向到标准输出;这里标准输出又被重定向到checkmem.log文件中。