PHP – CGI, Fast-FGI, PHP-FPM以及优化手段 – 学习/实践

  • Post author:
  • Post category:php


1.应用场景

主要用于了解学习cgi, fast-cgi,以及FPM,对应的php-cgi, php-fcgi, php-fpm之间的关系,

更加深入了解PHP与web server之间的工作机制~

2.学习/操作

1.文档阅读


Home – PHP-FPM


PHP: FastCGI 进程管理器(FPM) – Manual


PHP-FPM是什么? – 知乎


Why Do You Need PHP FastCGI Process Manager (PHP-FPM)?


http://wiki.php.net/rfc/fpm/ini_syntax

– Jérôme’s documentation and proposal of the new INI-based configuration format (what is used in PHP 5.3)


http://wiki.php.net/rfc/fpm

– Antony’s proposal for inclusion into PHP core (obviously accepted)


深入理解PHP之:Nginx 与 FPM 的工作机制

//

2016-04-06


Fast CGI 工作原理

//

2016-10-05

— 推荐阅读


[经验总结]避免PHP-FPM内存泄漏导致内存耗尽

//

2016-11-28


php-fpm的reload过程

//

2017-02-23


PHP-FPM,Nginx,FastCGI 之间的关系

//

2017-11-22


PHP FastCGI进程管理器PHP-FPM的架构 – 山上小和尚 – 博客园




— 推荐 – 20220509 补充


对于php-fpm和cgi,还有并发响应的理解

//

2017-11-23


全面了解CGI、FastCGI、PHP-FPM

//

2019-11-11


PHP-FPM+Nginx通信原理

//

2019-11-16


.html 的文件如何使用 php-fpm 执行

//

2019-11-23


nginx+php-fpm工作原理

//

2020-03-03

— 推荐


php-fpm应用与php-swoole应用进行通信

//

2020-04-20


https://www.siammm.cn/archives/229


// 推荐 — 排版更好

Note:


同一个公众号的文章, 确实有些炒旧饭的感觉, 不过能更加清楚地说明问题,也未尝不可接受~


但是在整理完日期之后,另有想法~~


补充 — 20210429 周四 公寓

PHP 官方手册


以 CGI 模式安装时


以 Apache 模块安装时


深入理解Zend SAPIs(Zend SAPI Internals) – 风雪之隅

// 鸟哥


https://blog.csdn.net/william_n/article/details/116269806


2021-04-24 – PHP – 多进程编程「含线程/协程」 – 学习/实践_穿素白衫的少年的博客-CSDN博客_php多线程和多进程实现

推荐 — 20220511 周三


找到国外资料的一个重要途径是通过维基百科来查询,然后查看下方的资料参考来源


https://en.wikipedia.org/wiki/FastCGI


FastCGI.com Archives


— 很推荐,资料很详细


Home – PHP-FPM


CGI、FastCGI、PHP-CGI和PHP-FPM之间的联系区别 – 流浪者


http://web.archive.org/web/20160320065247/http://www.fastcgi.com/drupal/

// PHP + Fastcgi


http://web.archive.org/web/20160305142334/http://www.fastcgi.com/drupal/node/5?q=node/10


https://en.wikipedia.org/wiki/PHP#PHPFPM

2. 整理输出

文章一:


一、


抛个砖


1、Web Server传递数据的方法

正式说CGI之前,先来了解一下Web Server传递数据的另外一种方法:PHP Module加载方式。相信都会想起Apache吧,初学php时,在windows上安装完php和Apache之后,为了让Apache能够解析php代码,我们会在Apache的配置文件(httpd.conf)中添加如下配置:​​​​​​​



#添加下边两行

LoadModule php5_module D:/php/php5apache2_2.dll

AddType application/x-httpd-php .php

# 修改如下内容

<IfModule dir_module>

DirectoryIndex index.php index.html

</IfModule>

当在linux环境下源码安装时,大致是这样:


# ./configure –with-mysql=/usr/local –with-apache=/usr/local/apache –enable-track-vars

其实原理都是,用LoadModule来加载php5_module,就是把php作为apache的一个子模块来运行。当通过web访问php文件时,apache就会调用php5_module来解析php代码。

那么,php5_module是如何将数据传给php的解析器来解析php代码的呢?

答案是:sapi

用一张图来看apache、php、sapi三者之间的关系:

从上面图中,我们看出了sapi就是这样的一个中间过程,sapi提供了一个和外部通信的接口,使得PHP可以和其他应用进行交互数据(apache,nginx等)。php默认提供了很多种sapi,常见的提供给apache和nginx的php5_module、CGI、FastCGI,给IIS的ISAPI,以及Shell的CLI。(httpd是Apache超文本传输协议(HTTP)服务器的主程序。被设计为一个独立运行的后台进程,它会建立一个处理请求的子进程或线程池)

所以,以上的apache调用php执行的过程如下:

apache -> httpd -> php5_module -> sapi -> php

这种模式将php模块安装到apache中,每一次apache请求,都会产生一条进程,这个进程就完整的包括php的各种运算计算等操作。

在上图中,我们很清晰的可以看到,apache每接收一个请求,都会产生一个进程来连接php通过sapi来完成请求,可想而知,如果一旦用户过多,并发数过多,服务器就会承受不住了。而且,把php当做一个模块加载到apache中,出问题时很难定位是php的问题还是apache的问题。


2、引出概念

在整个网站架构中,Web Server(如Apache、Nginx)只是内容的分发者。举个栗子,如果客户端请求的是 index.html,那么Web Server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据。

如果请求的是 index.php,根据配置文件,Web Server知道这个不是静态文件,需要去找 PHP 解析器来处理,那么他会把这个请求简单处理,然后交给PHP解析器。

当Web Server收到index.php 这个请求后,会启动对应的 CGI 程序,这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程,接下来再引出这些概念,会好理解很多。

CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。

FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。

PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。

PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。

(Web Server 一般指Apache、Nginx、IIS、Tomcat等服务器,Web Application 一般指PHP、Java、Asp.net等应用程序)


二、概念


1、

CGI

CGI(Common Gateway Interface)全称是“通用网关接口”,WEB 服务器与PHP应用进行“交谈”的一种工具,其程序须运行在网络服务器上。CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php、perl、tcl等。

WEB服务器会传哪些数据给PHP解析器呢?URL、查询字符串、POST数据、HTTP header都会有。所以,CGI就是规定要传哪些数据,以什么样的格式传递给后方处理这个请求的协议。也就是说,CGI就是专门用来和 web 服务器打交道的。web服务器收到用户请求,就会把请求提交给cgi程序(如php-cgi),cgi程序根据请求提交的参数作应处理(解析php),然后输出标准的html语句,返回给web服服务器,WEB服务器再返回给客户端,这就是普通cgi的工作原理。(cgi程序,你就可以理解成遵循cgi协议编写的程序)

优点:

CGI的好处就是完全独立于任何服务器,仅仅是做为中间分子。提供接口给web服务器和web应用(如提nginx和php)。他们通过cgi搭线来完成数据传递。这样做的好处了尽量减少2个的关联,使他们2变得更独立。

缺点:

但是CGI有个难受的地方,就是每一次web请求都会有启动和退出过程,也就是最为人诟病的fork-and-execute模式,这样一在大规模并发下,就死翘翘了。


2、FastCGI

从根本上来说,FastCGI是用来提高CGI程序性能的。类似于CGI,FastCGI也可以说是一种协议。

FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次。

FastCGI是和语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中,并因此获得较高的性能。众所周知,CGI解释器的反复加载是CGI性能低下的主要原因,如果CGI解释器保持在内存中,并接受FastCGI进程管理器调度,则可以提供良好的性能、伸缩性、Fail- Over特性等等。

举例:

当web server收到/index.php请求,看一下CGI程序和FastCGI程序分别是怎么处理的:

CGI:当收到web server请求后,会启动对应的CGI程序,这里就是PHP的解析器(php-cgi)。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定的CGI规定的格式返回处理后的结果,退出进程。(CGI每次接收到请求都会执行这些步骤)

FastCGI:首先,FastCGI程序会先启动一个master,解析配置环境,初始化执行环境,然后再启动多个worker。当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率自然是高。而且当worker不够用时,master可以根据配置预先启动几个worker等着;当然空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源,这就是fastcgi对进程的管理。(CGI程序和FastCGI程序,可以理解成遵循CGI协议和FastCGI协议编写的程序)

FastCGI的工作原理:

FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求,或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

(1)Web Server启动时载入FastCGI进程管理器(Apache Module或IIS ISAPI等)

(2)FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可建多个php-cgi),并等待来自Web Server的连接。

(3)当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。

(4)FastCGI子进程完成处理后,将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待,并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。在CGI模式中,php-cgi在此便退出了。

CGI与FastCGI比较:

(1)对于CGI来说,每一个Web请求PHP都必须重新解析php.ini、重新载入全部扩展,并重新初始化全部数据结构。而使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。

(2)由于FastCGI是多进程,所以比CGI多线程消耗更多的服务器内存,php-cgi解释器每进程消耗7至25兆内存,将这个数字乘以50或100就是很大的内存数。


3、

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 WEB服务器,目前最佳的方式是Apache/Nginx + FastCGI + PHP-FPM(+PHP-CGI)方式了。

参考:


https://segmentfault.com/a/1190000010968145


https://www.awaimai.com/371.html

PHP-FPM+Nginx通信原理

暂见下图

Image


上面的图Server2应该还是Browser,就是将结果返回给浏览器


——————————————————————————————————————————–



20220509 又重新看一遍



——————————————————————————————————————————–

先整理如下:很简单的话来总结概括

SAPI:

1. PHP Module

2. CGI

3. FastCGI

— 对CGI的优化,还是算作CGI范畴

4. ISAPI [IIS]

5. CLI [Shell]

CGI, FastCGI

php-cig [cgi and fastcgi both use

php-cgi

]


其实个人认为可以分为php-cig 和 php-fastcgi,知道这个事实就行了。

对于CGI来说,每一个Web请求PHP都必须重新解析php.ini、重新载入全部扩展,并重新初始化全部数据结构。同时,由于fork-and-execute的机制,并不能很好支持高并发。

而使用FastCGI,所有这些都只在进程启动时发生一次,一个额外的好处:持续数据库连接(Persistent database connection)可以工作,同时添加了进程池,有效提升了高并发能力。

PHP-FPM

PHP FastCGI的一种实现,添加了不少额外的有用辅助功能,由于其更加出色的表现,从PHP5.3起,已经被内置到PHP中。

PHP的进程管理器,主要管理master进程和workers进程,

这里的worker进程是php-cgi进程,具体干活的。

master进程是主进程,相当于leader,指挥协调的。

PHP-FPM常驻内存。

Note:


有时候一些图,会直接使用PHP-FPM进程来代表master进程和worker进程


如果,在命令行下,查看进程,会发现是php-fpm进程

PHP-FPM 是对于 FastCGI 协议的具体实现,他负责管理一个进程池,来处理来自Web服务器的请求。目前,PHP5.3版本之后,PHP-FPM是内置于PHP的。

每个worker进程能保持一个到MySQL/Memcached/Redis的持久连接, 实现”连接池”, 避免重复建立连接, 对程序透明. 使用数据库持久连接时应该设置固定数量的worker进程数, 不要使用动态的prefork模式.

见补充2


CGI, FastCGI


PHP-CGI, PHP-FastCGI, PHP-FPM之间的关系

PHP-FPM的处理流程

就是上面的图 —

PHP-FPM+Nginx通信原理

先插入

mac 终端中输入:


man php

可以看到出现的命令有:

php – PHP Command Line Interface ‘CLI’

php-cgi – PHP Common Gateway Interface ‘CGI’ command

执行命令如下





~

php -v

PHP 8.1.1 (

cli

) (built: Dec 17 2021 22:38:05) (NTS)

Copyright (c) The PHP Group

Zend Engine v4.1.1, Copyright (c) Zend Technologies

with Zend OPcache v8.1.1, Copyright (c), by Zend Technologies





~

php-cgi -v

PHP 8.1.1 (

cgi-fcgi

) (built: Dec 17 2021 22:38:39)

Copyright (c) The PHP Group

Zend Engine v4.1.1, Copyright (c) Zend Technologies

with Zend OPcache v8.1.1, Copyright (c), by Zend Technologies





~


About – PHP-FPM

其中有对比,以及说明选择PHP-FPM的原因


以及关于PHP-FPM的历史

后续补充

3.问题/补充

1. SAPI全称

Server Application Programming Interface: the API used by PHP to interface with Web Servers


https://github.com/laruence/phpbook/blob/master/1.1.md

2. PHP-FPM 工作细节



PHP FastCGI进程管理器PHP-FPM的架构 – 山上小和尚 – 博客园

一个master进程, 支持多个pool, 每个pool由master进程监听不同的端口, pool中有多个worker进程.

每个worker进程都内置PHP解释器, 并且进程常驻后台, 支持prefork动态增加.

每个worker进程支持在运行时编译脚本,

并在内存中


缓存


生成的opcode来提升性能.


每个worker进程支持配置

响应指定请求数后

自动重启, master进程会重启挂掉的worker进程.

每个worker进程能保持一个到MySQL/Memcached/Redis的持久连接, 实现”连接池”, 避免重复建立连接, 对程序透明. 「

只是一个持久连接吗?是的,参见


连接池 – 数据库短连接&长连接&连接池 – 学习/实践_穿素白衫的中少年的博客-CSDN博客



使用数据库持久连接时应该设置固定数量的worker进程数, 不要使用动态的prefork模式.


「应是为了避免动态的prefork模式,创建太多的worker进程,导致内存紧张,以及CPU上下文切换频繁,导致性能下降」




@syaokun219



@IM鑫爷

纠正,以下两句有误:

master进程采用epoll模型异步接收和分发请求,listen监听端口,epoll_wait等待连接.

然后分发给对应pool里的worker进程,worker进程accept请求后poll处理连接.


应该是:

master进程并不接收和分发请求, 而是worker进程直接accept请求后poll处理.

master进程不断调用


epoll_wait





getsockopt


是用来异步处理信号事件和定时器事件.


这里提一下, Nginx也类似, master进程并不处理请求, 而是worker进程直接处理,



不过区别在于Nginx的worker进程是epoll异步处理请求, 而PHP-FPM仍然是poll.


— 被标注部分

如果worker进程不够用, master进程会prefork更多进程,

如果prefork达到了pm.max_children上限, worker进程又全都繁忙,


这时master进程会把请求,挂起到连接队列backlog里(默认值是511).

1个PHP-FPM工作进程在同一时刻里只能处理1个请求.

MySQL的最大连接数max_connections默认是151.


只要PHP-FPM工作进程数不超过151, 就不会出现连接不上MySQL的情况.


而且正常情况下, 也不需要开启那么多的PHP-FPM工作进程,

比如,4个PHP-FPM进程就能跑满4个核心的CPU,


那么你开40个PHP-FPM进程也没有任何意义,

只会占用更多的内存, 造成更多的CPU上下文切换, 性能反而更差.


为了减少每个请求都重复建立和释放连接的开销, 可以开启持久连接,

一个PHP-FPM进程保持一个到MySQL的长连接, 实现透明的”连接池”.

Nginx跟PHP-FPM分开, 其实是很好的解耦, PHP-FPM专门负责处理PHP请求, 一个页面对应一个PHP请求,

页面中所有静态资源的请求都由Nginx来处理, 这样就实现了动静分离, 而Nginx最擅长的就是处理高并发.

PHP-FPM是一个多进程的FastCGI服务, 类似Apache的prefork的进程模型,

对于只处理PHP请求来说, 这种模型是很高效很稳定的.

不像Apache(libphp.so), 一个页面, 要处理多个请求, 包括图片, 样式表, JS脚本, PHP脚本等.

php-fpm从5.3开始才进入PHP源代码主干, 之前版本没有php-fpm.

那时的spawn-fcgi是一个需要调用php-cgi的FastCGI进程管理器,

另外像Apache的mod_fcgid和IIS的PHP Manager也需要调用php-cgi进程,


但php-fpm则根本不依赖php-cgi, 完全独立运行, 也不依赖php(cli)命令行解释器.



因为php-fpm是一个


内置了php解释器


的FastCGI服务, 启动时能够自行读取php.ini配置和php-fpm.conf配置.





所以,我们通常只要下载安装php-fpm即可,不需要再单独安装PHP




个人认为, PHP-FPM工作进程数, 设置为2倍CPU核心数就足够了.


「这是经过实践得出的一个适合的数字,针对具体的业务场景,未必是最佳,但是不差」


毕竟, Nginx和MySQL以及系统同样要消耗CPU.「

这句话的前提是Nginx和MYSQL,与PHP-FPM都部署在同一台机器上,现实中,Nginx和MySQL通常是分开的,至于PHP-FPM要不要单独部署,应视情况而定」


根据服务器内存,来设置PHP-FPM进程数非常不合理,



把内存分配给MySQL, Memcached, Redis, Linux磁盘缓存(buffers/cache)这些服务显然更合适.


过多的PHP-FPM进程反而会增加CPU上下文切换的开销.



PHP代码中应该尽量避免curl或者file_get_contents这些可能会产生较长网络I/O耗时的代码.


​​​​​​​


「但是实际是,很多服务涉及到网络请求和接收请求的参数,通常会用到curl和file_get_contents,但是应知道,这些网络I/O操作,确实可能会产生较长耗时」

注意设置CURLOPT_CONNECTTIMEOUT_MS超时时间, 避免进程被长时间阻塞.


如果要异步执行耗时较长的任务, 可以 pclose(popen(‘/path/to/task.php &’, ‘r’)); 打开一个进程来处理,



或者借助消息队列, 总之就是要尽量避免阻塞到PHP-FPM工作进程.


「异步执行耗时较长的任务,通过方式一,以往的工作中没见过,通常采用的是方式二


原因推测:方式一依然要消耗服务器的各种资源,而且存在消息/数据丢失的可能,而且代码耦合,也不利于服务扩展。


方式二,则可以使用独立的服务部署,解耦,适合扩展,降低或保证消息/数据不会丢失,方案很成熟」


​​​​​在php-fpm.conf中把request_slowlog_timeout设为1秒, 在slowlog中查看是否有耗时超过1秒的代码.


优化代码, 能够为所有PHP-FPM工作进程减负,这个才是提高性能的根本方法.

能让CPU满负荷运行的操作可以视为CPU密集型操作.


curl和下载

则是典型的I/O密集型操作, 因为耗时主要发生在网络I/O和磁盘I/O.

需要PHP认证的下载操作可以委托为

Nginx的AIO线程池:


header(“X-Accel-Redirect: $file_path”);

至于curl操作, 比如可以建立一个监听9001端口的名为upload的PHP-FPM进程池(pool),

专门负责处理curl操作(通过Nginx分发), 避免curl操作阻塞到监听9000端口的计算密集的www进程池.

这时upload进程池多开点进程也无所谓.

这里想达到的效果:

就是将计算密集型业务和I/O密集型业务分开,达到互不影响,从而达到资源的最大有效利用率。


另外,下面这篇文章,也可以佐证上面红色标注部分


21 | 为什么用了负载均衡更加不均衡?-极客时间

3.

PHP-FPM 的运作模式

4.参考

参见上面文档列表

后续补充



版权声明:本文为william_n原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。