系统:macOS 10.14.6
PHP:homebrew 安装的 PHP8.0.20 (fpm-fcgi)
除了有 php-fpm.conf 配置文件外,通常还有其他的 *.conf 配置文件(也可以不要,直接在 php-fpm.conf 配置)用于配置进程池,不同的进程池可以用不同的用户执行,监听不同的端口,处理不同的任务;多个进程池共用一个全局配置。
关于路径问题,原文是这样说的:
All relative paths in this configuration file are relative to PHP’s install prefix (/usr/local/Cellar/php@8.0/8.0.20). This prefix can be dynamically changed by using the ‘-p’ argument from the command line.
“此配置文件中的所有相对路径都相对于 PHP 的安装前缀。 可以使用命令行中的“-p”参数动态更改此前缀。”。
通过 phpinfo() 看到编译参数 ‘–prefix=/usr/local/Cellar/php@8.0/8.0.20’ ‘–localstatedir=/usr/local/var’
所以我的环境下,prefix=/usr/local/Cellar/php@8.0/8.0.20。按照他的说法,如果我使用以下设置:
pid = run/php-fpm.pid
error_log = log/php-fpm.log
那么应该出现以下两个文件
/usr/local/Cellar/php@8.0/8.0.20/run/php-fpm.pid
/usr/local/Cellar/php@8.0/8.0.20/log/php-fpm.log
但实际出现的是以下两个文件
/usr/local/var/run/php-fpm.pid
/usr/local/var/log/php-fpm.log
也就是说这两个指令的相对地址是/usr/local/var(从结果上对应“–localstatedir”)。看原文说明也特别说明了他们的相对地址是 /usr/local/var:
; Pid file
; Note: the default prefix is /usr/local/var
; Default Value: none
pid = run/php-fpm.pid
; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
; into a local file.
; Note: the default prefix is /usr/local/var
; Default Value: log/php-fpm.log
error_log = log/php-fpm.log
不明白是上下文为什么会有这种冲突。难道这两个指令是相对于 localstatedir?不管是他的问题还是我理解的问题,涉及的路径的问题,最好还是直接使用完整路径,放在哪了清晰明了。
pid = /usr/local/var/run/php8.0-fpm.pid
#fpm 主进程 pid 存放位置,进程会根据设置自动生成文件
error_log = /usr/local/var/log/php8/php-fpm.log
#错误日志存放位置,设置成本地文件即可,进程会根据设置自动生成文件
#若设置成"syslog",日志将发送给 syslogd。(syslogd 不熟悉可以无视)
针对 error_log = syslog 个人建议有明确需要再来关注这两个指令
;syslog.facility = daemon
#用于说明正在记录消息的程序是什么类型,
#syslogd 可以对不同类型的 facility(设备)信息采取不同的处理方式。
#当前环境默认:daemon(可能是因为我们通常是以守护进程的方式在运行 fpm)以下是可以被
#识别的值:
#kern 内核信息,首先通过 klogd 传递;
#user 用户进程;
#mail 邮件;
#daemon 后台进程;
#authpriv 授权信息;
#syslog 系统日志;
#lpr 打印信息;
#news 新闻组信息;
#uucp 由uucp生成的信息
#cron 计划和任务信息。
#mark syslog 内部功能用于生成时间戳
#local0----local7 与自定义程序使用,例如使用 local5 做为 ssh 功能
#扩展阅读:https://blog.csdn.net/zyy617532750/article/details/74942090
;syslog.ident = php-fpm
#当启用了多割进程池,这一项负责区分出不同的进程池消息,所有有多个进程池配置时,此项的值应该也不同
#当前环境默认:php-fpm
;log_level = notice
#记录日志的级别:alert、error、warning、notice、debug。当前环境默认:notice
;log_limit = 1024
#单行字符数。如果该行超出限制,则换行。当前环境默认:1024。
;log_buffering = yes 重日志场景关注此项
#google 翻译:日志缓冲指定日志行是否被缓冲,这意味着该行是在单个写入操作中写入的。
#如果值为 false,则数据直接写入文件描述符。这是一个实验性的选项,可以在一些繁重的日志
#记录场景中潜在地提高日志记录性能和内存使用率。如果记录到 syslog,则忽略此选项,
#因为它必须始终被缓冲。
#当前环境默认:yes
;emergency_restart_threshold = 0
;emergency_restart_interval = 0
#表示在 emergency_restart_interval 所设置的时间内(如设置:60s)出现SIGSEGV
#或者SIGBUS错误的 php-cgi 进程数如果超过了 emergency_restart_threshold 个,
#php-fpm 就会优雅重启。这对于解决加速器共享内存中的意外损坏非常有用。
#这两个选项一般保持默认值。0 表示 “关闭该功能”。 当前环境默认: 0 (关闭)。
;process_control_timeout = 0
#设置子进程等待主进程对信号做出反应的超时时间。
#默认单位:s(秒)。 默认值:0(不限制超时时间)。
; process.max = 0
#允许 FPM 可 fork 出的进程的最大数。
#当开启了多个进程池并在多个进程池中使用动态 PM(下面有讲)时,
#用来限制全局进程数。谨慎使用。默认:0,没有限制
; process.priority = -19
#设置子进程的优先级,在 master 进程以 root 用户启动时有效;如果没有设置,子进程会
#继承 master 进程的优先级,值范围-19(最高)到20(最低),默认不设置。
daemonize = yes
#设置 yes,FPM 将在后台运行;
#设置 no,FPM将在前台运行,此模式有助于调试时及时展示错误信息
当程序提示打开文件数量受到限制时,关注这两项。
在一些并发或多线程情况下,需要突破这个限制。
;rlimit_files = 1024
#设置 master 进程最多能打开的文件数。nginx.conf中也能看到相关指令。默认为系统的值。
;rlimit_core = 0
#设置 master 进程核心 rlimit 限制值;可选 unlimited 或 >=0 的整数,默认为系统的值。
#ulimit -n 和-u 可以查看 linux 的最大进程数和最大文件打开数。
;events.mechanism = epoll
#一直不知道干嘛的 默认自动检测
# Specify the event mechanism FPM will use. The following is available:
# - select (any POSIX os)
# - poll (any POSIX os)
# - epoll (linux >= 2.5.44)
# - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
# - /dev/poll (Solaris >= 7)
# - port (Solaris >= 10)
;systemd_interval = 10
#当 fpm 被设置为系统服务时,多久向服务器报告一次运行报告,单位有s、m、h。
#默认单位s,默认值10。(原来程序也要打卡上班的)默认即可,不用管他。
#在不同的监听端口和不同的管理选项下可以跑任意数量的池,并没有个数限制;
#进程池的名字用于 标记哪些属于本进程池进程的日志和统计数据。
#像 nginx 可以把不同的虚拟主机的配置写在自己的文件中然后再通过 include 引入一样
#多个进程池配置也可以单独写进不同的文件,引入文件时可以写相对路径,也可以写绝对路径
#默认只有 www.conf
include=/usr/local/etc/php/8.0/php-fpm.d/*.conf
当前环境下 include 引入的文件是 /usr/local/etc/php/8.0/php-fpm.d/www.conf 下文继续介绍 www.conf
[www]
#启动一个名为'www'的进程池,这个值会写入$pool变量,可以在本文中任何指令中使用,
#需要使用进程池名称的地方,都可以直接使用$pool作为代替。
;prefix = /path/to/pools/$pool 使用相对路径时关注下
#本配置文件中的相对路径前缀 prefix,默认不设置,将使用全局默认 prefix
#一般我喜欢设置完整路径
#只影响下面的指令(如果它们设置的是相对路径的话):
# - 'access.log'
# - 'slowlog'
# - 'listen' (unixsocket)
# - 'chroot'
# - 'chdir'
# - 'php_values'
# - 'php_admin_values'
user = nobody
group = nobody
#设置 worker 进程启动时的 user 和 group,必须要设置 user,而 group 在未设置的情
#况下,默认使用 user 所在的 group,默认都是 nobody,一般系统中也会存在这个用户和组
listen = 127.0.0.1:9999
#监听的 ip 和端口号。
#可以的格式如下:
# 'ip.add.re.ss:port' - 通过 a TCP socket 监听 IPv4:port
# '[ip:6:addr:ess]:port' - 通过 a TCP socket 监听 IPv6:port
# 'port' - 通过 a TCP socket 监听 以上所有地址类型
# '/path/to/unix/socket' - 通过 a unix socket 监听
;listen.backlog = 511 高并发时关注(注释是抄的,我没太理解)
#设置 listen(2) 函数的 backlog 参数值,默认: 511。
#未 accept 处理的 socket 队列大小,-1 on FreeBSD and OpenBSD,其他平台默认
#65535,高并发时重要,合理设置会及时处理排队的请求;太大会积压太多,处理完后
#nginx 在前面都等超时断开这个和 fpm 的 socket 连接了,就杯具了。
#不要用-1,建议1024以上,最好是2的幂值。
#一个池共用一个 backlog 队列,所有的池进程都去这个队列里 accept 连接。
#最大数量受限于系统配置 cat /proc/sys/net/core/somaxconn,系统配置修改:
#vim /etc/sysctl.conf,增加 net.core.somaxconn = 2000 则最大为2000,
#然后 php 最大的 backlog 可以到2000。
#http://blog.chinaunix.net/uid-28541347-id-5748886.html
针对 unix socket 连接方式的设置
;listen.owner = nobody
;listen.group = nobody
;listen.mode = 0660
#用 unix socket 连接方式时,指定拥有 unix socket 权限的用户,
#默认和运行的用户一样;用 tcp 连接可以注释掉
;listen.acl_users =
;listen.acl_groups =
#当支持 POSIX 访问控制列表时,您可以使用这些选项设置它们,值是用逗号分隔的用户/组
#名称的列表。 设置后,listen.owner 和 listen.group 将被忽略
;listen.allowed_clients = 127.0.0.1
#设置允许连接 fpm 的地址,多个地址用逗号隔开,如果不配置,则默认任意地址都能来连。
默认:any,任何主机都可以连接。仅对 tcp socket 有意义。
# Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable
# in the original PHP FCGI (5.2.2+).
; process.priority = -19 不理解优先级的用途,默认就好
#set nice(2) priority 参数。
#池进程的权限,同样要 master 进程是 root 用户才有效,和全局那个一样,
#不设置的话会继承 master 进程的优先级。默认不设置。
; process.dumpable = no 不知道实际用途,默认就好
#即使进程用户或组不同于主进程用户,也要设置进程可转储标志 (PR_SET_DUMPABLE prctl)。
#它允许为池用户创建进程核心转储和 ptrace 进程。默认值:no
pm = dynamic
#决定进程管理器以哪种算法来控制进程数量,提供了三种算法:
#static - 固定数量,数值由 pm.max_children 确定;
#dynamic - 子进程的数量是根据以下指令动态设置的。有了这个流程管理,总会有至少1个子进程:
# pm.max_children - 可以同时活跃的最大子进程数。
# pm.start_servers - fpm 启动时创建的子进程数量(就是初始值)
# pm.min_spare_servers - 空闲子进程的最小值,低于这个值就会创建新的子进程
# pm.max_spare_servers - 空闲子进程的最大值,高于这个值就会kill部分子进程
#ondemand - fpm 启动时不创建子进程. 有连接请求时才会创建子进程,涉及指令:
# pm.max_children - 可以同时活跃的最大子进程数。
# pm.process_idle_timeout - 指定空闲进程空闲多久后被killed
#默认dynamic 小内存建议dynamic 8G以上建议static 因为创建和回收进程也是有开销的
#pm.max_children 等效于带有 mpm_prefork 的 ApacheMaxClients 指令。等效于原始
#PHP CGI 中的 PHP_FCGI_CHILDREN 环境变量。以下默认值基于没有太多资源的服务器。
pm.max_children = 5 #dynamic static ondemand
pm.start_servers = 2 #dynamic
#计算公式:(min_spare_servers + max_spare_servers) / 2
pm.min_spare_servers = 1 #dynamic
pm.max_spare_servers = 3 #dynamic
;pm.process_idle_timeout = 10s; #ondemand 默认值10s
;pm.max_requests = 500
#每个子进程最大处理500请求就被回收,可防止内存泄露。默认没有开启,进程永远不重启。
#等效于 PHP_FCGI_MAX_REQUESTS
非常有意思的功能(默认关闭状态),fpm 提供了获取进程运行状态的入口,并提供了一个静态页面来展示这些数据。文章结尾有设置参考
;pm.status_path = /status #负责开启或关闭 FPM status page
;pm.status_listen = 127.0.0.1:9001 #这是干嘛的 反正默认就好了
;ping.path = /ping
#FPM 监控页面的 ping 网址。 如果没有设置,则无法访问 ping 页面。该页面用于外部
#检测 FPM 是否存活并且可以响应请求。请注意必须以斜线开头 (/)。默认没有设置
;ping.response = pong
#用于定义 ping 请求的返回相应. 返回为 HTTP 200 的 text/plain 格式文本。
#默认值:pong。
;access.log = log/$pool.access.log
#类似于 nginx 的访问日志,fpm 也有访问日志,每次有请求过来都可以记录日志,默认关闭
;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
#此处定义访问日志的格式,具体格式参见原文
;slowlog = log/$pool.log.slow #php慢日志 默认关闭
;request_slowlog_timeout = 0 #超过指定时间的请求将被写进慢日志 默认0
;request_slowlog_trace_depth = 20 #慢日志堆栈信息深度 默认20
;request_terminate_timeout = 0
#设置单个请求的超时中止时间. 该选项可能会对php.ini设置中的'max_execution_time'因为
#某些特殊原因没有中止运行的脚本有用. 设置为 '0' 表示 'Off'.当经常出现502错误时可以尝试
#更改此选项。默认为0
;request_terminate_timeout_track_finished = no
# 在应用程序调用 'fastcgi_finish_request' 或应用程序完成并调用关闭函数(通过
# register_shutdown_function 注册)后,不使用 'request_terminate_timeout' ini
# 选项设置的超时。 即使在这种情况下,此选项也将启用无条件应用超时限制。 默认no关闭
#和上面含义一样 按照上下文的意思 只限制当前线程池 默认系统默认值
;rlimit_files = 1024
;rlimit_core = 0
;chroot =
#启动时的chroot目录. 所定义的目录需要是绝对路径. 如果没有设置, 则chroot不被使用.
#默认没有设置
;chdir = /var/www
#设置启动目录,启动时会自动chdir到该目录. 所定义的目录需要是绝对路径. 默认值: 当前目录,
#或者/目录(chroot时)
#注意:chroot和chdir是两个系统命令,开启这两项时,php-fpm在启动时会执行这两个命令,而参数值就是此处设置的值,至于实际用途不是很理解,暂时保持默认好了。
;catch_workers_output = yes
#重定向运行过程中的stdout和stderr到主要的错误日志文件中. 如果没有设置, stdout
#和 stderr 将会根据FastCGI的规则被重定向到 /dev/null . 默认值: no.
;decorate_workers_output = yes
#使用前缀和后缀装饰工作输出,其中包含有关写入日志的子节点的信息,以及是否使用了 stdout 或
#stderr 以及日志级别和时间。 仅当 catch_workers_output 为 yes 时才使用此选项。
#设置为“no”将输出写入 stdout 或 stderr 的数据。
;clear_env = no #清理环境 默认yes 默认即可
;security.limit_extensions = .php .php3 .php4 .php5 .php7
#设置fpm执行解析的扩展名 默认 .php
#传递环境变量,如 LD_LIBRARY_PATH。 所有 $VARIABLEs 都取自当前环境。
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
#php.ini 的附加设置,只应用于这个进程池。这些设置会覆盖之前在 php.ini 中定义的值。
#此处若定义 disable_functions 或者 disable_classes ,会将新的设置附加在原有值的后面。
#使用 php_admin_value 或者 php_admin_flag 定义的值,不能被 PHP 代码中的 ini_set() 覆盖。
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
;php_admin_value[error_log] = /var/log/fpm-php.www.log
;php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M
pid = /usr/local/var/run/php8.0-fpm.pid
error_log = /usr/local/var/log/php8/php-fpm.log
log_level = notice
daemonize = yes
user = nobody
group = nobody
listen = 127.0.0.1:9999
listen.allowed_clients = ipaddress_1,ipaddress_2,127.0.0.1,...
pm = dynamic 同时关注下其他相关设置
pm.max_requests = 0 按需要选择是否开启
访问日志 默认关闭
access.log = log/$pool.access.log 按需要选择是否开启
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
慢日志 默认关闭
slowlog = log/$pool.log.slow
request_slowlog_timeout = 15
request_slowlog_trace_depth = 20
一般原则是:动态适合小内存机器,灵活分配进程,省内存。静态适用于大内存机器,动态创建回收进程对服务器资源也是一种消耗。
如果你的内存很大,有8-20G,按照一个 php-fpm 进程 20M 算,100个就2G内存了,那就可以开启 static 模式。如果你的内存很小,比如才256M,那就要小心设置了,因为你的机器里面的其他的进程也算需要占用内存的,所以设置成 dynamic 是最好的,比如:pm.max_chindren = 8, 占用内存160M左右,而且可以随时变化,对于一般访问量的网站足够了。
我们有时候会经常饱受500,502问题困扰。当 Nginx 收到如上错误码时,可以确定后端 php-fpm 解析 php 出了某种问题,比如,执行错误,执行超时。
这个时候,我们是可以开启慢日志功能的。
slowlog = /usr/local/var/log/php-fpm.log.slow
request_slowlog_timeout = 15s
当一个请求该设置的超时时间15秒后,就会将对应的PHP调用堆栈信息完整写入到慢日志中。
php-fpm慢日志会记录下进程号,脚本名称,具体哪个文件哪行代码的哪个函数执行时间过长:
[21-Nov-2013 14:30:38] [pool www] pid 11877
script_filename = /usr/local/lnmp/nginx/html/www.quancha.cn/www/fyzb.php
[0xb70fb88c] file_get_contents() /usr/local/lnmp/nginx/html/www.quancha.cn/www/fyzb.php:2
通过日志,我们就可以知道第2行的 file_get_contents 函数有点问题,这样我们就能追踪问题了。
适用场景:nginx 和 php-fpm 在同一台服务器上,这时可以直接用 unixs ocket 进行进程间通信,不走 tcp 端口通信,可以节约创建连接的时间,从而提高性能。
1、设置 php-fpm 的 listen 为 /you_path/php-fpm.sock,然后重启 fpm 就会自动创建该文件
2、nginx 的 fastcgi_pass 参数修改为 unix:/you_path/php-fpm.sock;通过 php-fpm.sock 文件去和 fpm 通信,需要保证该 nginx 有权限访问 php-fpm.sock 文件 。
sock 文件随便创建到哪里都可以,只要 fpm 有权限在那个目录里写文件,nginx有权限去读就可以。tcp 连接会更稳定,因为有 tcp 协议保证数据的正确性,但是sock 有更少的数据拷贝和上下文切换,更少的资源占用。不过只能在 nginx 和fpm 在同一台机器上才能用 sock。
一个 fpm 子进程在同一时间只能处理一个请求,如果 backlog 设置得过大,nginx之类的客户端发起的请求一直没有 fpm 子进程进行 accept,nginx 就会直接断掉这个连接,等 fpm 忙过来了再去 accept 的时候,就会发现断开了,于是报错。backlog 设置得过小,访问量大时 fpm 子进程全部处于忙碌状态,backlog 也塞满了,就会拒绝新的连接,此时 nginx 再请求,就会直接被拒。所以需要合理的设置 backlog 参数。
大部分参数只要系统默认的就可以了,我们只需要知道几个比较重要的参数设置,什么时候用到可以回头查询相关说明
在php-fpm的配置文件中,有两个指令非常重要,就是"pm.max_children" 和 “request_terminate_timeout”
第一个指令"pm.max_children" 确定了php-fpm的处理能力,原则上时越多越好,但这个是在内存足够打的前提下,每开启一个php-fpm进程要占用近30M左右的内存
如果请求访问较多,那么可能会出现502,504错误。对于502错误来说,属于繁忙进程而造成的,对于504来说,就是客户发送的请求在限定的时间内没有得到相应,过多的请求导致“504 Gateway Time-out”。这里也有可能是服务器带宽问题。
另外一个需要注意的指令"request_terminate_timeout",它决定php-fpm进程的连接/发送和读取的时间,如果设置过小很容易出现"502 Bad Gateway" 和 “504 Gateway Time-out”,默认为0,就是说没有启用,不加限制,但是这种设置前提是你的php-fpm足够健康,这个需要根据实际情况加以限定。