小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
进程管理的三种模式
先说结论,PHP-FPM 的进程管理一共有三种模式:
-
ondemand
-
static
-
dynamic
该结论是从 PHP-FPM 的配置文件中得出,在 PHP-FPM 的配置文件中,我们可以看到下面一段内容:
; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives. With this process management, there will be
; always at least 1 children.
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; ondemand - no children are created at startup. Children will be forked when
; new requests will connect. The following parameter are used:
; pm.max_children - the maximum number of children that
; can be alive at the same time.
; pm.process_idle_timeout - The number of seconds after which
; an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic
复制代码
配置文件解读
从上述配置文件中,我们可以知道 pm
参数负责进程的管理模式,共有三种可能的选项,分别为:ondemand、static、dynamic。且该值必须设置。
-
static
:固定数量(由pm.max_children
参数控制)的子进程; -
dynamic
:子进程的数量根据下面的指令动态设定,但是在这种进程管理模式下,至少会有一个子进程。 -
pm.max_children
:同时存活的 worker 子进程最大数量; -
pm.start_servers
:启动时创建的 worker 子进程数量; -
pm.min_spare_servers
:空闲状态(等待进程)的 worker 子进程的最小数量,如果空闲状态 worker 子进程小于该值,会创建一些子进程(直到达到该值)。 -
pm.max_spare_servers
:空闲状态(等待进程)的 worker 子进程的最大数量,如果空闲状态 worker 子进程大于该值,会杀死一些子进程(直到达到该值)。 -
ondemand
:在启动时不会创建任何 worker 子进程。只有当新的连接进来时,才会 fork 出新的子进程(注意:该触发条件是连接的到来,而不是实际的请求。例如,只进行连接比如 Telnet,不发送请求数据也会新建 worker 进程)。在该模式下会用到下面两个参数: -
pm.max_children
:同时存活的 worker 子进程最大数量; -
pm.process_idle_timeout
:多长时间后,一个空闲的子进程会被杀掉。
其它相关参数设置如下:
-
pm.max_children
:pm
设置为static
时表示创建的 worker 进程数量,pm
设置为dynamic
或者ondemand
时,表示最大可创建的 worker 进程数量。 -
pm.start_servers
:设置启动时创建的 worker 子进程的数量,仅在pm
设置为dynamic
时使用。 -
pm.min_spare_servers
:设置空闲 worker 进程的最小数量,仅在pm
设置为dynamic
时使用。 -
pm.max_spare_servers
:设置空闲 worker 进程的最大数量,仅在pm
设置为dynamic
时使用。 -
pm.process_idle_timeout
:多久之后结束空闲的 worker 进程。 仅当pm
设置为ondemand
时使用。
优缺点及限制条件
ondemand
优点
- 按流量需求创建,不浪费系统之源
缺点
- 由于 PHP-FPM 是短连接,所以每次请求都会先建立连接,建立连接的过程会耗费系统之源,当流量很大时,频繁的创建销毁进程开销很大,不适合大流量环境部署。
限制条件
- worker 进程的数量受限于
pm.max_children
配置,同时受限于全局配置process.max
(准确的说,三种模式都受限于全局配置)
static
优点
-
方法简单;
-
避免了频繁开启关闭进程的开销;
缺点
- 如果配置成 static,只需要考虑 max_children 数量,数量取决于 CPU 的个数和应用的响应时间,一次启动固定大小进程浪费系统之源。
限制条件
- worker 进程的数量受限于全局配置
process.max
(准确的说,三种模式都受限于全局配置)
dynamic
优点
- 动态扩容,不浪费系统资源。
缺点
-
当所有的 worker 进程都在工作时,新的请求到来需要等待创建 worker 进程,最长等待 1s(内部存在一个 1s 的定时器,去查看,创建进程)。
-
会频繁的启动停止进程,消耗系统资源(当请求数稳定时,不需要频繁销毁);
限制条件
- worker 进程的数量受限于
pm.max_children
配置,同时受限于全局配置process.max
(准确的说,三种模式都受限于全局配置)
其他相关参数说明
pm.max_requests
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
;pm.max_requests = 500
复制代码
该参数的含义是指一个 worker 子进程在处理多少个请求后就终止掉。此时 master 进程会重新创建一个新的 worker 进程。
该配置主要是为了避免 PHP 解释器或程序引用的第三方库造成的内存泄漏。
pm.status_path
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. It shows the following informations:
; pool - the name of the pool;
; process manager - static, dynamic or ondemand;
; start time - the date and time FPM has started;
; start since - number of seconds since FPM has started;
; accepted conn - the number of request accepted by the pool;
; listen queue - the number of request in the queue of pending
; connections (see backlog in listen(2));
; max listen queue - the maximum number of requests in the queue
; of pending connections since FPM has started;
; listen queue len - the size of the socket queue of pending connections;
; idle processes - the number of idle processes;
; active processes - the number of active processes;
; total processes - the number of idle + active processes;
; max active processes - the maximum number of active processes since FPM
; has started;
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic' and 'ondemand');
; Value are updated in real time.
; Example output:
; pool: www
; process manager: static
; start time: 01/Jul/2011:17:53:49 +0200
; start since: 62636
; accepted conn: 190460
; listen queue: 0
; max listen queue: 1
; listen queue len: 42
; idle processes: 4
; active processes: 11
; total processes: 15
; max active processes: 12
; max children reached: 0
复制代码
;pm.status_path = /status
复制代码
我们可以通过访问 /status
接口来获取 FPM 进程池状态。其中有几个关系字段需要注意一下:
-
pool
:FPM 进程池名称,大部分为 www。Master 进程可以管理多个 pool,每个 pool 可以配置为三种模式的任意一种,即 Master 进程同时支持多种进程管理模式。 -
listen queue
:请求等待队列,如果该值不为 0,可以考虑增加下 FPM 进程数量。 -
max children reached
:达到进程最大数量限制的次数,如果这个数量不为 0,那说明你的最大进程数量太小了,请改大一点。
生产环境配置示例
[pool_name1]
pid = /home/service/php/var/run/xxx.pid
error_log = /home/service/php/log/xxx.log
syslog.facility = daemon
syslog.ident = php-fpm
log_level = warning
emergency_restart_threshold = 100
emergency_restart_interval = 30m
process_control_timeout = 1m
daemonize = yes
rlimit_files = 65535
;rlimit_core = 0
rlimit_core = unlimited
events.mechanism = epoll
[pool_name2]
user = work
group = work
listen = 监听端口
access.log=/home/work/logs/xxx.log
pm = ondemand
pm.max_children = 750
pm.process_idle_timeout = 10s
pm.max_requests = 500
pm.status_path = /status
ping.path = /ping
ping.response = pong
catch_workers_output = yes
security.limit_extensions = .php
request_slowlog_timeout = 1s
request_terminate_timeout = 10s
slowlog = /home/service/php/log/xxx
php_flag[display_errors] = off
php_admin_value[error_log] = /home/work/logs/xxx
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 512M
复制代码
实践演示
ondemand 演示
当 pm = ondemand
时,我们重启 PHP-FPM,然后查看此时 FPM 进程:
# ps -ef | grep fpm
root 32636 1 0 11:41 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
root 32653 32636 0 11:42 pts/0 00:00:00 grep fpm
复制代码
可以看到,此时没有 worker 进程。
当我们执行一下请求后,可以看到多了一个 worker 子进程。
# ps -ef | grep fpm
root 32636 1 0 11:41 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
work 33146 32636 15 11:48 ? 00:00:00 php-fpm: pool www
root 33151 32584 0 11:48 pts/0 00:00:00 grep fpm
复制代码
一段时间后,我们再去查看,发现这个 worker 进程没有了,与上述内容一致。
# ps -ef | grep fpm
root 32636 1 0 11:41 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
root 33208 32636 0 11:52 pts/0 00:00:00 grep fpm
复制代码
那我们把 master 进程 kill 掉会怎么样呢?我们来试验一下。
kill -9 之后,再次请求,此时 Nginx 提示 502,表示服务不可用。
static 演示
当我们改为如下配置时:
pm = static
pm.max_children = 2
pm.max_requests = 5
复制代码
[root@yz-dev-inf00 ~]# ps -ef | grep fpm
root 34467 1 0 12:08 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
work 34468 34467 0 12:08 ? 00:00:00 php-fpm: pool www
work 34469 34467 0 12:08 ? 00:00:00 php-fpm: pool www
root 34473 32584 0 12:08 pts/0 00:00:00 grep fpm
复制代码
我们可以看到启动了两个 worker 进程。此时可以正常请求。那么此时我们 kill 掉 worker 进程会怎么样呢?
# kill 34468
# ps -ef | grep fpm
root 34467 1 0 12:08 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
work 34469 34467 0 12:08 ? 00:00:00 php-fpm: pool www
work 34501 34467 0 12:10 ? 00:00:00 php-fpm: pool www
root 34503 32584 0 12:10 pts/0 00:00:00 grep fpm
复制代码
可以看到了又新启动一个 worker 进程。
那么我们 kill -9 主进程呢?
# kill -9 34523
# ps -ef | grep fpm
work 34524 1 0 12:12 ? 00:00:00 php-fpm: pool www
work 34525 1 0 12:12 ? 00:00:00 php-fpm: pool www
root 34540 32584 0 12:13 pts/0 00:00:00 grep fpm
复制代码
可以看到,子进程仍然在,并且仍然能正常请求。只不过此时 2 个 worker 进程变成孤儿进程,被 pid = 1 的进程收养,但是整个 PHP-FPM 能够继续提供服务
由于我们的 pm.max_requests = 5
,当请求了 5 次之后,只剩下一个 worker 进程了。然后我们再把最后一个 worker 进程 kill 掉,就无法正常请求了。
dynamitc 演示
pm =dynamic
pm.min_spare_servers=1
pm.max_spare_servers=2
复制代码
此时我们查看 FPM 进程:
# ps -ef | grep fpm
root 35056 1 0 12:20 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
work 35057 35056 0 12:20 ? 00:00:00 php-fpm: pool www
root 35059 32584 0 12:20 pts/0 00:00:00 grep fpm
复制代码
我们执行一下 kill -9 worker 进程命令,发现又重新启动了一个 worker 进程。
# kill -9 35057
# ps -ef | grep fpm
root 35056 1 0 12:20 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
work 35072 35056 0 12:21 ? 00:00:00 php-fpm: pool www
root 35074 32584 0 12:21 pts/0 00:00:00 grep fpm
复制代码
那么我们 kill -9 主进程呢?
# ps -ef | grep fpm
root 35056 1 0 12:20 ? 00:00:00 php-fpm: master process (/home/service/php/etc/php-fpm.conf)
work 35072 35056 0 12:21 ? 00:00:00 php-fpm: pool www
root 38428 38406 0 13:41 pts/1 00:00:00 grep fpm
# kill -9 35056
# ps -ef | grep fpm
work 35072 1 0 12:21 ? 00:00:00 php-fpm: pool www
root 38434 38406 0 13:41 pts/1 00:00:00 grep fpm
复制代码
还是同上面一样,worker 进程变成了孤儿进程,被 pid=1 的进程收养,但是仍然可以提供服务。
总结
-
PHP-FPM 进程管理的三种模式是什么?
-
ondemand:按需启动
-
static:固定数量
-
dynamic:动态数量
-
PHP-FPM 如何创建 worker 进程?
-
ondemand:
-
启动时不创建 worker 进程,需要的时候再创建,最多同时存在
pm.max_children
个子进程。 -
static:
-
启动时创建
pm.max_children
个子进程。当有 worker 进程关闭,再重新创建 worker 进程,使总数量为pm.max_children
。 -
dynamic:
-
动态模式,至少有 1 个 worker 进程;
-
启动 pm.start_servers 个,最多可以启动 pm.max_children 个。
-
如果子进程有空闲,且个数小于 pm.min_spare_servers,则补齐到 pm.min_spare_servers,如果大于 pm.max_spare_servers,则降低到 pm.max_spare_servers。
-
如何查看 PHP-FPM 进程状态?
- 在 php-fpm.conf 文件中配置
pm.status_path = /status
,即可查看 FPM 状态。
-
PHP-FPM 中是 worker 进程处理请求还是 master 进程处理请求?master 进程被 kill 掉还能正常接收请求吗?
-
是 worker 进程接收请求,master 进程负责管理 worker 进程。
-
master 进程被
kill -9
后,worker 进程还存在,还可以处理请求,当 worker 进程不存在时,报 502 Gateway 错误。 -
master 进程被
kill
后,其 worker 子进程也会被 kill,不能提供服务。 -
具体看
kill
和kill -9
区别。 -
pm.max_requests
参数有什么作用?
- 为了防止 PHP 解释器或程序引用的第三方库造成内存泄漏,所以当该 worker 进程处理请求数量达到
pm.max_requests
后,便杀死该进程。