static 静态模式,启动的时候创建固定数量的worker 进程,实际请求大于worker进程的时候 包warning
ondemand 按需分配模式,启动的时候不会创建worker进程,根据需要创建,释放在idle_timeout之后
这样不能及时的释放连接和建立连接需要消耗资源
dynamic 动态模式(默认):启动的时候创建指定数量的worker进程,根据情况合理的worker,定期检查worker,关闭闲置连接
PHP-FPM(FastCGI Process Manager)是一个PHPFastCGI进程管理器,从其英文名称和定义可以看出,FPM的核心功能就是进程管理。
FastCGI可以理解为一种协议,用于web服务器(nginx、Apache)和处理程序间进行通信,是一种应用层通信协议。
PHP在web方式中如何改了文件就立即生效的,重要的几个概念:
● sapi: 可以简单的理解为php引擎对外的一个统一接口,使得php可以和外部程序进行交互
● php的生命周期中关键四个调用: MINT -> RINT -> RSHUTDOWN -> MSHUTDOWN
数据初始化(mint)==》请求初始化(rint) ==》编译脚本(rshuntdown) ==》执行代码(mshutdown)
● fpm: fastcgi进程管理器
fpm通过sapi接口与php进程交互
1.fpm启动会调用各扩展的MINT方法,进行一些数据初始化(长驻内存)
2.每个请求过来,先会执行RINT对单个请求行一个初始化
3.执行php脚本(在没有缓存opcode的情况下,这里的php脚本是动态执行的,所以更新php脚本后,会执行新的php脚本,详情不在这里叙述)
4.执行RSHUTDOWN方法
5.如果你要停止fpm了,才会执行MSHUTDOWN
fpm对每个请求的处理都是一直在在重复执行 2~4步,在第三步中,php的脚本是动态执行的,由于每次都要执行一次php脚本,而每次php脚本都要有一个把php文件翻译成opcode的流程(比较耗时), 于是就产生的opcache工具。
opcache
直接把php翻译后的opcode代码树保存到共享内存中,以便直接使用,从而减少每次都把php翻译成opcode的开销。
opcache的问题: 按照他的描述,修改了php文件,并不能立即被更新。
opcache的解决方案: 有一个配置来设置隔多长时间检测文件是否更新了,从而有机会在第二步重新来reload相关的文件。
当然,直接reload fpm,从而达到php热更新的效果(opcache扩展可以在第四步把相关的opcode cache给清空)。
worker的工作流程包含以下几个步骤
int main(int argc, char *argv[])
{
...变量定义,参数初始化
//注册SAPI
sapi_startup(&cgi_sapi_module);
...
//执行php_module_starup()
if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
return FPM_EXIT_SOFTWARE;
}
//初始化
if(0 > fpm_init(...)){
//记录日志并退出
return FPM_EXIT_CONFIG;
}
...
fpm_is_running = 1;//fpm运行状态标识
fcgi_fd = fpm_run(&max_requests);//进程初始化,调用fork()创建work进程
...
fcgi_init_request(&request, fcgi_fd); //初始化请求;
//此阶段的php_request_startup()会调用每个扩展的:PHP_RINIT_FUNCTION();
if (UNEXPECTED(php_request_startup() == FAILURE)) {
...
}
...
php_fopen_primary_script(&file_handle TSRMLS_CC); //打开脚本;
...
php_execute_script(&file_handle TSRMLS_CC); //执行脚本;
...
//worker进程退出
php_module_shutdown();
...
}
PHP7 默认是
pm=dynamic
pm.max_children=50
pm.start_servers = 5
动态方式下的起始php-fpm进程数量
只有当pm的配置为dynamic时候才会有效如果为static,会忽略此参数
php启动的时候,开启子进程的数量。具体的子进程的数量会根据请求的变化发生变化,但是最大不会超过pm.max_children配置的数值。
min_spare_servers + (max_spare_servers - min_spare_servers) / 2;
一般而言,设置成10-20之间的数据足够满足需求了。
pm.max_children = 50
表示php-fpm能启动的子进程的最大数
static下只有这一个参数生效
计算方式
一般来说一台服务器正常情况下每一个php-cgi所耗费的内存在20M~30M左右,因此我的”max_children”我设置成40个,20M*40=800M也就是说在峰值的时候所有PHP-CGI所耗内存在800M以内,低于我的有效内存2Gb。
而如果我 的”max_children”设置的较小,比如5-10个,那么php-cgi就会“很累“,处理速度也很慢,等待的时间也较长,占用的CPU也很高。
如果长时间没有得到处理的请求就会出现 504 Gateway Time-out 这个错误,而正在处理的很累的那几个php-cgi如果遇到了问题就会出现 502 Bad gateway 这个错误。
max_children较好的设置方式根据req/s(吞吐率,单位时间里服务器处理的最大请求数,单位req/s)来设置,若程序是 100 req/s 的处理能力,那么就设置 100比较好,这是动态来调整的。
pm.max_spare_servers
动态方式下空闲状态最大php-fpm数量
pm.max_spare_servers的值只能小于等于pm.max_children
系统会在php-fpm运行开始时启动pm.start_servers个php-fpm进程,然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。
pm.process_idle_timeout = 10s
worker 空闲多少秒之后被kill,按需分配模式dynamic
默认10s
pm.max_requests=500
每个进程处理多少个请求之后自动终止,可以有效防止内存溢出,如果为0则不会自动终止,默认为0#设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 ‘0’ 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0
pm.status_path
注册的URI,以展示php-fpm状态的统计信息
在php-fpm.conf中打开
在nginx中配置
server {
......
# 在 server 中添加以下配置
location = /status {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
}
.....
}
pool – fpm池子名称,大多数为www
process manager – 进程管理方式,值:static, dynamic or ondemand. dynamic
start time – 启动日期,如果reload了php-fpm,时间会更新
start since – 运行时长
accepted conn – 当前池子接受的请求数
listen queue – 请求等待队列,如果这个值不为0,那么要增加FPM的进程数量
max listen queue – 请求等待队列最高的数量
listen queue len – socket等待队列长度
idle processes – 空闲进程数量
active processes – 活跃进程数量
total processes – 总进程数量
max active processes – 最大的活跃进程数量(FPM启动开始算)
max children reached - 进程最大数量限制的次数,如果这个数量不为0,那说明你的最大进程数量太小了,请改大一点。
slow requests – 启用了php-fpm slow-log,缓慢请求的数量
ping.path
ping url,可以用来测试php-fpm是否存活并可以响应
ping.response
ping url的响应正文返回为 HTTP 200 的 text/plain 格式文本. 默认值: pong.
ping.response = pong
11)pid = run/php-fpm.pid
#pid设置,默认在安装目录中的var/run/php-fpm.pid,建议开启
error_log = log/php-fpm.log
#错误日志,默认在安装目录中的var/log/php-fpm.log
log_level = notice
#错误级别. 可用级别为: alert(必须立即处理), error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice.
emergency_restart_threshold = 60
emergency_restart_interval = 60s
#表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项一般保持默认值。
process_control_timeout = 0
#设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0.
daemonize = yes
#后台执行fpm,默认值为yes,如果为了调试可以改为no。在FPM中,可以使用不同的设置来运行多个进程池。 这些设置可以针对每个进程池单独设置。
listen = 127.0.0.1:9000
#fpm监听端口,即nginx中php处理的地址,一般默认值即可。可用格式为: ‘ip:port’, ‘port’, ‘/path/to/unix/socket’. 每个进程池都需要设置.
listen.backlog = -1
#backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。 19)listen.allowed_clients = 127.0.0.1
#允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程,listen处要设置成本地可被访问的IP。默认值是any。每个地址是用逗号分隔. 如果没有设置或者为空,则允许任何服务器请求连接
listen.owner = www
listen.group = www
listen.mode = 0666
#unix socket设置选项,如果使用tcp方式访问,这里注释即可。
user = www
group = www
#启动进程的帐户和组
request_terminate_timeout = 0
#设置单个请求的超时中止时间. 该选项可能会对php.ini设置中的’max_execution_time’因为某些特殊原因没有中止运行的脚本有用. 设置为 ‘0’ 表示 ‘Off’.当经常出现502错误时可以尝试更改此选项。
request_slowlog_timeout = 10s
#当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 ‘0’ 表示 ‘Off’
slowlog = log/$pool.log.slow
#慢请求的记录日志,配合request_slowlog_timeout使用
rlimit_files = 1024
#设置文件打开描述符的rlimit限制. 默认值: 系统定义值默认可打开句柄是1024,可使用 ulimit -n查看,ulimit -n 2048修改。
rlimit_core = 0
#设置核心rlimit最大限制值. 可用值: ‘unlimited’ 、0或者正整数. 默认值: 系统定义值.
chroot =
#启动时的Chroot目录. 所定义的目录需要是绝对路径. 如果没有设置, 则chroot不被使用.
chdir =
#设置启动目录,启动时会自动Chdir到该目录. 所定义的目录需要是绝对路径. 默认值: 当前目录,或者/目录(chroot时)
catch_workers_output = yes
#重定向运行过程中的stdout和stderr到主要的错误日志文件中. 如果没有设置, stdout 和 stderr 将会根据FastCGI的规则被重定向到 /dev/null . 默认值: 空.
php-fpm启动的时候,创建固定的worker数量
该模式比较简单,在启动时按照配置pm.max_children启动固定数量的的进程,这些进程阻塞进行请求的接收
方法执行流程:
fpm_run()->fpm_children_create_initial()->fpm_children_make()
启动fpm的时候会调用fpm_run方法,而fpm_run方法内部会调用子进程初始化方法fpm_children_create_initial,
在该方法内部会调用fpm_children_make方法创建worker进程。
php-fpm启动采用固定大小数量的worker,在运行期间也不会扩容,虽然也有1秒的定时器,仅限于统计一些状态信息,例如空闲worker个数,活动worker个数,网络连接队列长度等信息。
pm=static
pm.max_children=3
static的时候只有这个参数生效
方法执行流程:
fpm_run()->fpm_children_create_initial()->fpm_children_make()
启动fpm的时候会调用fpm_run方法,而fpm_run方法内部会调用子进程初始化方法fpm_children_create_initial,
在该方法内部会调用fpm_children_make方法创建worker进程
1、pm.max_children> 0 必须配置,且只有这一个参数生效
如果配置成static,只需要考虑max_children的数量,数量取决于cpu的个数和应用的响应时间,我司配置的是50。
我司不考虑动态的增加减少那么十几个或者几十个worker,我们的内存没有紧张到这个程度,所以,我们一步到位,把worker数配置到支持最大流量,(哈哈,50也是随便定的,足矣足矣呢)
pm = ondemand
pm.process_idle_timeout = 60 #worker 空闲多少秒之后被kill,按需分配模式dynamic
pm.max_children=3
ondemand原理图
\1. 从上图可以看出,新建worker的触发条件是连接的到来,而不是实际的请求(例如,只进行连接比如telnet,不发请求数据也会新建worker)
\2. worker的数量受限于pm.max_children配置,同时受限全局配置process.max(准确的说,三种模式都受限于全局配置)
3.1秒定时器作用
找到空闲worker,如果空闲时间超过pm.process_idle_timeout大小,关闭。这个机制可能会关闭所有的worker。
\1. pm.max_children> 0
\2. pm.process_idle_timeout> 0,如果不设置,默认10s
优点:按流量需求创建,不浪费系统资源(在硬件如此便宜的时代,这个优点略显鸡肋)
缺点:由于php-fpm是短连接的,所以每次请求都会先建立连接,建立连接的过程必然会触发上图的执行步骤,所以,在大流量的系统上master进程会变得繁忙,占用系统cpu资源,不适合大流量环境的部署
ondemand模式,运行流程和第一步相同,不同之处是在第二个函数中不会分配work进程,而是注册了一个事件回调函数fpm_pctl_on_socket_accept(),部分代码如下:
if (wp->config->pm == PM_STYLE_ONDEMAND) {
wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
......
memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
fpm_event_set(wp->ondemand_event,wp->listening_socket,FPM_EV_READ|FPM_EV_EDGE,fpm_pctl_on_socket_accept, wp);
......
}
以上代码片段只保留了部分本文所需要的内容
ondemand模式work进程的创建,回调函数fpm_pctl_on_socket_accept()的部分代码如下:
if (wp->running_children >= wp->config->pm_max_children) { //判断进程数是否超过最大限制
......
return;
}
for (child = wp->children; child; child = child->next) {
//fpm_request_is_idle函数返回return proc->request_stage == FPM_REQUEST_ACCEPTING
if (fpm_request_is_idle(child)) {
return; // FPM_REQUEST_ACCEPTING代表处于等待请求阶段
}
}
......
fpm_children_make(wp, 1, 1, 1);//创建work进程
ondemand模式work进程的关闭
PFM注册了一个定时事件fpm_pctl_perform_idle_server_maintenance_heartbeat检查当前模式下work进程的运行情况,当空闲进程等待请求时间超过pm_process_idle_timeout后,会对最后一个空闲worker进程发出关闭信号,此操作由主进程进行处理,部分代码如下:
if (wp->config->pm == PM_STYLE_ONDEMAND) {
struct timeval last, now;
if (!last_idle_child) continue;//最后一个idle进程
......
// last.tv_sec为上次接收请求的时间
if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
last_idle_child->idle_kill = 1;
fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
}
continue;
}
dynamic
模式:动态模式dynamic模式,启动时分配固定数量的work进程,然后随着请求的增加会增加进程数,此模式下几个重要的配置项如下:
max_children 最大进程数
pm_max_spare_servers 允许最大的空闲进程数
min_spare_servers 允许最小的空闲进程数
start_servers 启动时的进程数
执行过程和ondemand模式类似,启动时主进程都会创建一个定时事件来定时检查work的运行状况,不同的是dynamic模式初始化的时候会创建一定数量的进程,而ondemand模式不会创建
pm = dynamic
pm.max_children = 3
pm.start.servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 6
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZehrV8gR-1579172905068)(PHP-FPM.assets/image-20200116174736466.png)]
dynamic原理图
\1. 1秒定时器作用
检查空闲worker数量,按照一定策略动态调整worker数量,增加或减少。增加时,worker最大数量<=max_children· <=全局process.max;减少时,只有idle >pm.max_spare_servers时才会关闭一个空闲worker。
idle > pm.max_spare_servers,关闭启动时间最长的一个worker,结束本次处理
idle >= pm.max_children,打印WARNING日志,结束本次处理
idle < pm.max_children,计算一个num值,然后启动num个worker,结束本次处理
\1. pm.min_spare_servers/pm.max_spare_servers有效范围(0,pm.max_children]
\2. pm.max_children> 0
\3. pm.min_spare_servers<=pm.max_spare_servers
\4. pm.start_servers有效范围[pm.min_spare_servers,pm.max_spare_servers]如果没有配置,默认pm.min_spare_servers + (pm.max_spare_servers - pm.min_spare_servers) / 2
优点:动态扩容,不浪费系统资源,master进程设置的1秒定时器对系统的影响忽略不计;
缺点:如果所有worker都在工作,新的请求到来只能等待master在1秒定时器内再新建一个worker,这时可能最长等待1s;
static 静态模式,启动的时候创建固定数量的worker 进程,实际请求大于worker进程的时候 包warning
ondemand 按需分配模式,启动的时候不会创建worker进程,根据需要创建,释放在idle_timeout之后
这样不能及时的释放连接和建立连接需要消耗资源
dynamic 动态模式(默认):启动的时候创建指定数量的worker进程,根据情况合理的worker,定期检查worker,关闭闲置连接
参考文件,如有不合适的,请告知删除
链接:https://www.jianshu.com/p/c9a028c834ff
https://www.toutiao.com/a6776929604788552196/?tt_from=mobile_qq&utm_campaign=client_share×tamp=1579141890&app=news_article&utm_source=mobile_qq&utm_medium=toutiao_android&req_id=202001161031300100080431041DC7F156&group_id=6776929604788552196
https://blog.csdn.net/njrclj/article/details/85062459