PHP-FPM 进程管理的三种模式

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

进程管理的三种模式

先说结论,PHP-FPM 的进程管理一共有三种模式:

  1. ondemand

  2. static

  3. 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_childrenpm 设置为 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

优点

  1. 按流量需求创建,不浪费系统之源

缺点

  1. 由于 PHP-FPM 是短连接,所以每次请求都会先建立连接,建立连接的过程会耗费系统之源,当流量很大时,频繁的创建销毁进程开销很大,不适合大流量环境部署。

限制条件

  1. worker 进程的数量受限于 pm.max_children 配置,同时受限于全局配置 process.max(准确的说,三种模式都受限于全局配置)

static

优点

  1. 方法简单;

  2. 避免了频繁开启关闭进程的开销;

缺点

  1. 如果配置成 static,只需要考虑 max_children 数量,数量取决于 CPU 的个数和应用的响应时间,一次启动固定大小进程浪费系统之源。

限制条件

  1. worker 进程的数量受限于全局配置 process.max(准确的说,三种模式都受限于全局配置)

dynamic

优点

  1. 动态扩容,不浪费系统资源。

缺点

  1. 当所有的 worker 进程都在工作时,新的请求到来需要等待创建 worker 进程,最长等待 1s(内部存在一个 1s 的定时器,去查看,创建进程)。

  2. 会频繁的启动停止进程,消耗系统资源(当请求数稳定时,不需要频繁销毁);

限制条件

  1. 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 的进程收养,但是仍然可以提供服务。

总结

  1. PHP-FPM 进程管理的三种模式是什么?

  2. ondemand:按需启动

  3. static:固定数量

  4. dynamic:动态数量

  5. PHP-FPM 如何创建 worker 进程?

  6. ondemand:

  7. 启动时不创建 worker 进程,需要的时候再创建,最多同时存在 pm.max_children 个子进程。

  8. static:

  9. 启动时创建 pm.max_children 个子进程。当有 worker 进程关闭,再重新创建 worker 进程,使总数量为 pm.max_children

  10. dynamic:

  11. 动态模式,至少有 1 个 worker 进程;

  12. 启动 pm.start_servers 个,最多可以启动 pm.max_children 个。

  13. 如果子进程有空闲,且个数小于 pm.min_spare_servers,则补齐到 pm.min_spare_servers,如果大于 pm.max_spare_servers,则降低到 pm.max_spare_servers。

  14. 如何查看 PHP-FPM 进程状态?

  • 在 php-fpm.conf 文件中配置 pm.status_path = /status,即可查看 FPM 状态。
  1. PHP-FPM 中是 worker 进程处理请求还是 master 进程处理请求?master 进程被 kill 掉还能正常接收请求吗?

  2. 是 worker 进程接收请求,master 进程负责管理 worker 进程。

  3. master 进程被 kill -9 后,worker 进程还存在,还可以处理请求,当 worker 进程不存在时,报 502 Gateway 错误。

  4. master 进程被 kill 后,其 worker 子进程也会被 kill,不能提供服务。

  5. 具体看 killkill -9 区别。

  6. pm.max_requests 参数有什么作用?

  • 为了防止 PHP 解释器或程序引用的第三方库造成内存泄漏,所以当该 worker 进程处理请求数量达到 pm.max_requests 后,便杀死该进程。

参考文档

猜你喜欢

转载自juejin.im/post/7018583928978014245