PHP fpm
要了解php-fpm
相关知识,必须要先了解cgi
、fast-cgi
相关知识。
早期的web服务器,可以简单的响应浏览器发送来的http
请求,并返回对应存储在服务器上的html
文档给浏览器,这种就是静态的html
。
随着时间的变化,静态资源满足不了一些日常需求,网站也越来越复杂,所以出现了动态技术。但是服务器并不能直接运行php
、asp
这些的文件,需要对应的语言去处理对应格式的文件,例如如果请求的是.php
文件,需要交给php
去处理。这交给php
去处理时候,就出现了问题,怎么交给php
处理,以及php
处理后怎么返回,需要什么样的格式、需要什么样的数据等等。于是,就需要一个统一的标准去规定和协调,两个不同程序间通讯的协议,CGI(Common gateway interface)
就出现了。
CGI
就像一个翻译机,给web
服务器翻译对应的php
语言,便于相互之间的理解和通讯,最后才会响应给客户端。
WEB
服务器将根据CGI
程序的类型决定数据向CGI
程序的传送方式,一般来讲是通过标准输入/输出流和环境变量来与CGI
程序间传递数据。 如下图所示:
CGI
程序通过标准输入(STDIN
)和标准输出(STDOUT
)来进行输入输出。此外CGI
程序还通过环境变量来得到输入,操作系统提供了许 多环境变量,它们定义了程序的执行环境,应用程序可以存取它们。
CGI
CGI
全称是 公共网关接口(Common Gateway Interface)
,HTTP
服务器与你的或其它机器上的程序进行交谈的一种工具,其程序须运行在网络服务器上。
CGI
可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl
等。
CGI
是HTTP Server
和一个独立的进程之间的协议,把HTTP Request
的Header
设置成进程的环境变量,HTTP Request
的正文设置成进程的标准输入,而进程的标准输出就是HTTP Response
包括Header
和正文。
F-CGI
FastCGI
像是一个**常驻(long-live
)**型的CGI
,它可以一直执行着,只要激活后,不会每次都要花费时间去fork
一次(这是CGI
最为人诟病的fork-and-execute
模式)。它还支持分布式的运算,即 FastCGI
程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。
FastCGI
是语言无关的、可伸缩架构的CGI
开放扩展,其主要行为是将CGI
解释器进程保持在内存中并因此获得较高的性能。众所周知,CGI
解释器的反复加载是CGI
性能低下的主要原因,如果CGI
解释器保持在内存中并接受FastCGI
进程管理器调度,则可以提供良好的性能、伸缩性、Fail- Over
特性等等。
FASTCGI
是和HTTP
协议类似的概念。无非就是规定了在同一个TCP
连接里怎么同时传多个HTTP
连接。这实际上导致了个问题,有个HTTP
连接传个大文件不肯让出FASTCGI
连接,在同一个FASTCGI
连接里的其他HTTP
连接就傻了。所以Lighttpd
引入了 X-SENDFILE
。
Fast-Cgi的特点
fast-cgi
具有语言无关性。
FastCGI
在进程中的应用程序,独立于核心web
服务器运行,提供了一个比API
更安全的环境。APIs
把应用程序的代码与核心的web
服务器链接在一起,这意味着在一个错误的API
的应用程序可能会损坏其他应用程序或核心服务器。 恶意的API
的应用程序代码甚至可以窃取另一个应用程序或核心服务器的密钥。
FastCGI
技术目前支持语言有:C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby
等。相关模块在Apache, ISS, Lighttpd
等流行的服务器上也是可用的。
FastCGI
的不依赖于任何Web
服务器的内部架构,因此即使服务器技术的变化, FastCGI
依然稳定不变。
FastCGI的工作原理
FastCGI
接口方式采用C/S
结构,可以将HTTP
服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP
服务器每次遇到动态程序时,可以将其直接交付给FastCGI
进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP
服务器专一地处理静态请求,或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。
Web Server
启动时载入FastCGI
进程管理器(Apache Module
或IIS ISAPI
等)FastCGI
进程管理器自身初始化,启动多个CGI
解释器进程(可建多个php-cgi
),并等待来自Web Server
的连接。- 当客户端请求到达
Web Server
时,FastCGI
进程管理器选择并连接到一个CGI
解释器。Web server
将CGI
环境变量和标准输入发送到FastCGI
子进程php-cgi
。 FastCGI
子进程完成处理后,将标准输出和错误信息从同一连接返回Web Server
。当FastCGI
子进程关闭连接时,请求便告处理完成。FastCGI
子进程接着等待,并处理来自FastCGI
进程管理器(运行在Web Server
中)的下一个连接。在CGI
模式中,php-cgi
在此便退出了。
CGI与Fast-CGI比较
- 对于
CGI
来说,每一个Web
请求PHP
都必须重新解析php.ini
、重新载入全部扩展,并重新初始化全部数据结构。而使用FastCGI
,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection
)可以工作。 - 由于
FastCGI
是多进程,所以比CGI
多线程消耗更多的服务器内存,php-cgi
解释器每进程消耗7至25兆内存,将这个数字乘以50或100就是很大的内存数。
PHP-FPM
首先要说的是:fastcgi
是一个协议,php-fpm
实现了这个协议。
大家都知道,PHP
的解释器是php-cgi
。php-cgi
只是个CGI
程序,他自己本身只能解析请求,返回结果,不会进程管理,所以就出现了一些能够调度php-cgi
进程的程序,php-fpm
就是这样的一个东西。它克服了php-cgi
变更php.ini
配置后,需重启php-cgi
才能让新的php-ini
生效,不可以平滑重启,直接杀死php-cgi
进程,php
就不能运行了的问题。修改php.ini
之后,php-cgi
进程的确没办法平滑重启的。php-fpm
对此的处理机制是新的worker
用新的配置,已经存在的worker
处理完手上的活就可以歇着了,通过这种机制来平滑过度。
php-fpm
提供了更好的php
进程管理方式,可以有效的控制内存和进程,可以平滑重载php
配置。
总结一下这个升级的过程:
php-fpm
是php
提供给web serve
也就是http
前端服务器的fastcgi
协议接口程序,它不会像php-cgi
一样每次连接都会重新开启一个进程,处理完请求又关闭这个进程,而是允许一个进程对多个连接进行处理,而不会立即关闭这个进程,而是会接着处理下一个连接。它可以说是php-cgi
的一个管理程序,是对php-cgi
的改进。
php-fpm
会开启多个php-cgi
程序,并且php-fpm
常驻内存,每次web serve
服务器发送连接过来的时候,php-fpm
将连接信息分配给下面其中的一个子程序php-cgi
进行处理,处理完毕这个php-cgi
并不会关闭,而是继续等待下一个连接,这也是fast-cgi
加速的原理,但是由于php-fpm
是多进程的,而一个php-cgi
基本消耗7-25M内存,因此如果连接过多就会导致内存消耗过大,引发一些问题,例如nginx
里的502错误。
同时php-fpm
还附带一些其他的功能:
例如平滑过渡配置更改,普通的php-cgi
在每次更改配置后,需要重新启动才能初始化新的配置,而php-fpm
是不需要,php-fpm
分将新的连接发送给新的子程序php-cgi
,这个时候加载的是新的配置,而原先正在运行的php-cgi
还是使用的原先的配置,等到这个连接后下一次连接的时候会使用新的配置初始化,这就是平滑过渡。
总结
一般
web
服务器接受到浏览器的请求时,如果是静态资源的话就直接将其返回给浏览器,如果是动态资源的话那就没有现成的资源返回了,那这个时候cgi
就出场了cgi
可以理解为一种协议or
一类处理程序,就是动态去生成文件,从程序上来理解就是web
服务器exec
这样一个进程,然后交给他一些输入参数,他就慢慢的处理完后把结果返回给web
服务器,那从协议层面来说cgi
协议就是规范了web
服务器和cgi
程序的一些输入输出参数的含义所以可以有很多不同的
cgi
程序,可以执行php
脚本的or可以执行python
脚本的,只要符合这类规范就能供web
服务器调用,当然它的缺点就是每次都需要去启动这个cgi
程序,这会使得处理速度很慢针对这种缺陷加以改进就成了
fastcgi
,同样的他也可以理解为一种协议or
一个程序,它跟cgi
的不同就是不需要每次去exec
,它会事先启动起来,作为一个cgi
的管理服务器存在,预先启动一系列的子进程来等待处理,然后等待web
服务器发过来的请求,一旦接受到请求就交由子进程处理,这样由于不需要在接受到请求后启动cgi
,会快很多。phpfpm
是php
对fastcgi
的一种具体实现,它的启动后会创建多个cgi
子进程,然后主进程负责管理子进程,同时它对外提供一个socket
,那web
服务器当要转发一个动态请求时只需要按照fastcgi
协议要求的格式将数据发往这个socket
的就可以了,那phpfpm
创建的子进程去争抢这个socket
连接,谁抢到了谁处理并将结果返回给web
服务器,那phpfpm
主进程干什么了?比方说其中一个子进程异常退出了怎么办,那phpfpm
会去监控他一旦发现一个cgi
子进程就会又启动一个,还有其他诸多管理功能phpfpm
作为一个独立的进程存在 通过socket
与nginx
建立连接,而mod_php
是作为一个模块被加载进了apache
服务器,同时他们两作为cgi
调度管理器,他们对其管理的方式也不一样。通俗的可以把服务器看作餐厅,用户请求看作来用餐的顾客,服务器处理请求看作解决顾客的就餐问题(响应输出一份饭)。
服务器上静态资源看作已做好的饭,只要放到餐盒里就可以返回给顾客,动态资源需要厨房大厨现成做份再放到餐盒里返回给顾客。
php_mod
这个大厨有个特点,看见有顾客进门就点火,不管顾客要不要现做的,有点浪费资源php_fpm
这个大厨有好多小弟一直点着火(多个处理进程),等有顾客说要现做,大厨就安排小弟做份返回给客户cgi
也是个大厨,不过他等到顾客要现做,他才点火,做饭,然后熄火。等待下一个要现做的到来fastcgi
呢就是个大厨雇了一帮小弟,专门做需要现场做的饭,大厨只管分派任务,小弟真正操锅做饭。PHP-FPM相关参数
全局配置
pid
stringpid
文件位置,默认为空error_log
string错误日志的位置。默认:
#INSTALL_PREFIX#/log/php-fpm.log
。 如果设置为syslog
,日志将不会写入本地文件,而是发送到syslogd
。log_level
错误级别。可用级别为:
alert
(必须立即处理),error
(错误情况),warning
(警告情况),notice
(一般重要信息),debug
(调试信息)。默认:notice
。syslog.facility
string设置何种程序记录消息,默认值:daemon。
syslog.ident
string为每条信息添加前缀。 如果在同一台服务器上运行了多个 FPM 实例,可以修改此默认值来满足需求。默认值:
php-fpm
。emergency_restart_threshold
int如果子进程在
emergency_restart_interval
设定的时间内收到该参数设定次数的SIGSEGV
或者SIGBUS
退出信息号,则FPM
会重新启动。0 表示“关闭该功能”。默认值:0(关闭)。emergency_restart_interval
emergency_restart_interval
用于设定平滑重启的间隔时间。这么做有助于解决加速器中共享内存的使用问题。可用单位:s
(秒),m
(分),h
(小时)或者d
(天)。默认单位:s
(秒)。默认值:0(关闭)。process_control_timeout
设置子进程接受主进程复用信号的超时时间。可用单位:
s
(秒),m
(分),h
(小时)或者d
(天)。默认单位:s
(秒)。默认值:0(关闭)。process.max
intFork
的最大 FPM 进程数。使用动态管理进程数时,此设计可以控制在一个进程池内的全局进程数量。 使用需谨慎。默认值:0。process.priority
int设置
master
进程的nice
(2) 优先级(如果设置了此值)。 可以是 -19(最高优先级)到 20 (更低优先级)。 默认值:不设置。daemonize
boolean设置
FPM
在后台运行。设置“no
”将 FPM 保持在前台运行用于调试。默认值:yes
。rlimit_files
int设置
master
进程的打开文件描述符rlimit
数。rlimit_core
int设置
master
进程最大core
的rlimit
尺寸。 默认值:0。events.mechanism
string设置
FPM
使用的事件机制。 可用以下选项:select
、pool
、epoll
、kqueue (*BSD)
、port (Solaris)
。 默认值:不设置(自动检测)systemd_interval
int使用
systemd
集成的FPM
时,设置间歇秒数,报告健在通知给systemd
。 设置为 0 表示禁用。默认值:10。运行配置区段
在
FPM
中,可以使用不同的设置来运行多个进程池。 这些设置可以针对每个进程池单独设置。
php-fpm
配置文件中每一个[www]
就代表一个进程池设置。
可以通过ps aux | grep php | grep -v grep
查看相关进程池。
1 |
|
可以看出,目前就启动了一个进程池为pool www
。
而每个进程池的配置参数如下:
listen
string
设置接受 FastCGI
请求的地址。可用格式为:ip:port
,port
,/path/to/unix/socket
。每个进程池都需要设置。
listen.backlog
int
设置 listen
的 backlog
最大值。“-1”表示无限制。默认值:-1。
listen.allowed_clients
string
设置允许连接到 FastCGI
的服务器 IPV4
地址。等同于 PHP FastCGI (5.2.2+)
中的 FCGI_WEB_SERVER_ADDRS
环境变量。仅对 TCP 监听起作用。每个地址是用逗号分隔,如果没有设置或者为空,则允许任何服务器请求连接。默认值:any
。 PHP 5.5.20
和 5.6.4
起,开始支持 IPv6 地址。
listen.owner
string
如果使用了 Unix
套接字,表示它的权限。在 Linux
中必须设置读/写权限,以便用于 WEB
服务器连接。 在很多 BSD
派生的系统中可以忽略权限允许自由连接。 默认值:运行所使用的用户和组,权限为 0660
。
listen.group
string
参见 listen.owner
。
listen.mode
string
参见 listen.owner
。
listen.acl_users
string
当系统支持 POSIX ACL
(Access Control Lists
)时,可以设置使用此选项。 当设置了的时候,将会忽略 listen.owner
和 listen.group
。 值是逗号分割的用户名列表。 PHP 5.6.5
起可用。
listen.acl_groups
string
参见 listen.acl_users
。 值是逗号分割的用户组名称列表。 PHP 5.6.5
起可用。
user
string
FPM
进程运行的Unix
用户。必须设置。
group
string
FPM
进程运行的 Unix
用户组。如果不设置,就使用默认用户的用户组。
pm
string
设置进程管理器如何管理子进程。可用值:static
,ondemand
,dynamic
。必须设置。
static
- 子进程的数量是固定的(pm.max_children
)。
ondemand
- 进程在有需求时才产生(当请求时才启动。与 dynamic 相反,在服务启动时 pm.start_servers
就启动了。
dynamic
- 子进程的数量在下面配置的基础上动态设置:pm.max_children
,pm.start_servers
,pm.min_spare_servers
,pm.max_spare_servers
。
pm.max_children
int
pm
设置为 static
时表示创建的子进程的数量,pm
设置为 dynamic
时表示最大可创建的子进程的数量。必须设置。
该选项设置可以同时提供服务的请求数限制。类似 Apache
的 mpm_prefork
中 MaxClients
的设置和 普通PHP FastCGI
中的 PHP_FCGI_CHILDREN
环境变量。
pm.start_servers
int
设置启动时创建的子进程数目。仅在 pm
设置为 dynamic
时使用。默认值:min_spare_servers + (max_spare_servers - min_spare_servers) / 2
。
pm.min_spare_servers
int
设置空闲服务进程的最低数目。仅在 pm
设置为 dynamic
时使用。必须设置。
pm.max_spare_servers
int
设置空闲服务进程的最大数目。仅在 pm
设置为 dynamic
时使用。必须设置。
pm.process_idle_timeout
秒数,多久之后结束空闲进程。 仅当设置 pm
为 ondemand
。 可用单位:s
(秒),m
(分),h
(小时)或者 d
(天)。默认单位:10s
。
pm.max_requests
int
设置每个子进程重生之前服务的请求数。对于可能存在内存泄漏的第三方模块来说是非常有用的。如果设置为 ‘0’ 则一直接受请求,等同于 PHP_FCGI_MAX_REQUESTS
环境变量。默认值:0。
pm.status_path
string
FPM
状态页面的网址。如果没有设置,则无法访问状态页面,默认值:无。
ping.path
string
FPM
监控页面的 ping 网址。如果没有设置,则无法访问 ping
页面。该页面用于外部检测 FPM
是否存活并且可以响应请求。请注意必须以斜线开头(/)。
ping.response
string
用于定义 ping
请求的返回响应。返回为 HTTP 200
的 text/plain
格式文本。默认值:pong。
process.priority
int
设置 worker
的 nice
(2)优先级(如果设置了的话)。 该值从 -19(最高优先级) 到 20(更低优先级)。 默认值:不设置
prefix
string
检测路径时使用的前缀。
request_terminate_timeout
设置单个请求的超时中止时间。该选项可能会对 php.ini
设置中的 max_execution_time
因为某些特殊原因没有中止运行的脚本有用。设置为 ‘0’ 表示 Off
。可用单位:s
(秒),m
(分),h
(小时)或者 d
(天)。默认单位:s
(秒)。默认值:0(关闭)。
request_slowlog_timeout
当一个请求该设置的超时时间后,就会将对应的 PHP
调用堆栈信息完整写入到慢日志中。设置为 ‘0’ 表示 Off
。可用单位:s
(秒m
(分),h
(小时)或者 d
(天)。默认单位:s
(秒)。默认值:0(关闭)。
slowlog
string
慢请求的记录日志。默认值:#INSTALL_PREFIX#/log/php-fpm.log.slow
。
rlimit_files
int
设置文件打开描述符的 rlimit
限制。默认值:系统定义值。
rlimit_core
int
设置核心 rlimit
最大限制值。可用值:’unlimited
‘,0 或者正整数。默认值:系统定义值。
chroot
string
启动时的 Chroot
目录。所定义的目录需要是绝对路径。如果没有设置,则 chroot
不被使用。
chdir
string
设置启动目录,启动时会自动 Chdir
到该目录。所定义的目录需要是绝对路径。默认值:当前目录,或者根目录(chroot
时)。
catch_workers_output
boolean
重定向运行过程中的 stdout
和 stderr
到主要的错误日志文件中。如果没有设置,stdout
和 stderr
将会根据 FastCGI
的规则被重定向到 /dev/null
。默认值:无。
clear_env
boolean
为 FPM worker
进程清除环境变量。 在进程池配置文件里设置环境变量前,阻止任意系统的环境变量进入 FPM worker
进程。 自 PHP 5.4.27
、 5.5.11
和 5.6.0
起。 默认值: Yes
security.limit_extensions
string
限制 FPM
允许解析的脚本扩展名。 此设置可以预防 web
服务器配置的错误。 应当限制 FPM
仅仅解析 .php
扩展名,阻止恶意用户使用其他扩展名运行 php
代码。 默认值: .php .phar
access.log
string
Access log
文件。 默认值:不设置
access.format
string
access log
的格式。 默认值: %R - %u %t \"%m %r\" %s
主要优化参数
他们分别是:pm
、pm.max_children
、pm.start_servers
、pm.min_spare_servers
、pm.max_spare_servers
。
pm
:表示使用那种方式,有两个值可以选择,就是static
(静态)或者dynamic
(动态)。
在更老一些的版本中,dynamic
被称作apache-like
。这个要注意看配置文件的说明。
下面4个参数的意思分别为:
pm.max_children
:静态方式下开启的php-fpm
进程数量或者动态模式下最大php-fpm
进程数pm.start_servers
:动态方式下的起始php-fpm
进程数量pm.min_spare_servers
:动态方式下的最小空闲php-fpm
进程数pm.max_spare_servers
:动态方式下的最大空闲php-fpm
进程数量
对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache
一样,运行的PHP
程序在执行完成后,或多或少会有内存泄露的问题。
这也是为什么开始的时候一个php-fpm
进程只占用3M
左右内存,运行一段时间后就会上升到20-30M
的原因了。
对于内存大的服务器(比如8G
以上)来说,指定静态的max_children
实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。
因为频繁开关php-fpm
进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M
得到,比如8GB
内存可以设置为100
,
那么php-fpm
耗费的内存就能控制在 2G-3G
的样子。如果内存稍微小点,比如1G
,那么指定静态的进程数量更加有利于服务器的稳定。
这样可以保证php-fpm
只获取够用的内存,将不多的内存分配给其他应用去使用,会使系统的运行更加畅通。
对于小内存的服务器来说,比如256M
内存的VPS
,即使按照一个20M
的内存量来算,10个php-cgi
进程就将耗掉200M内存,那系统的崩溃就应该很正常了。
因此应该尽量地控制php-fpm
进程的数量,大体明确其他应用占用的内存后,给它指定一个静态的小数量,会让系统更加平稳一些。或者使用动态方式,
因为动态方式会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或VPS上使用。具体最大数量根据 内存/20M
得到。
比如说512M
的VPS
,建议pm.max_spare_servers
设置为20
。至于pm.min_spare_servers
,则建议根据服务器的负载情况来设置,比如服务器上只是部署php环境的话,比较合适的值在5~10
之间。
同时设置每个子进程重生之前服务的请求数,pm.max_request
对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 ’0′ 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS
环境变量. 默认值: 0.
这段配置的意思是,当一个 PHP-CGI
进程处理的请求数累积到 500
个后,自动重启该进程。