cgi fastcgi PHP-CGI与php-fpm区别和之间的关系


CGI

Common Gateway Interface 公共网关接口,通俗的讲,CGI是为了保证Web Server传递过来的数据是标准格式的,协议只是一个“规定、规则”,理论上用什么语言都能实现,比如用 vb/c/perl/php/python 来实现

通过 CGI 接口,Web Server就能够获取客户端提交的信息,并转交给服务器端的 CGI 程序处理,最后返回结果给客户端。也就是说,CGI 实际上是一个接口标准。我们通常所说的 CGI 是指 CGI 程序,即实现了 CGI 接口标准的程序。只要某种语言具有标准输入、输出和环境变量,如 perl/PHP/C 等,就可以用来编写 CGI 程序。CGI 只是接口协议,根本不是什么语言

CGI程序的工作方式

Web Server一般只处理静态文件请求(如 jpg、htm、html),如果碰到一个动态脚本请求(如 php),Web Server主进程,就 fork 出一个新的进程来启动 CGI 程序,也就是说将动态脚本请求交给 CGI 程序来处理。
启动 CGI 程序需要一个过程,比如,读取配置文件,加载扩展等。CGI 程序启动后,就会解析动态脚本,然后将结果返回给 Web Server,最后 Web Server再将结果返回给客户端,刚才 fork 的进程也会随之关闭。这样,每次用户请求动态脚本,Web Server都要重新 fork 一个新进程,去启动 CGI 程序,由 CGI 程序来处理动态脚本,处理完后进程随之关闭。毫无疑问,这种工作方式的效率是非常低下的。运行示意图如下:

CGI

CGI程序通过标准输入(STDIN)和标准输出(STDOUT)来进行输入输出。此外CGI程序还通过环境变量来得到输入,操作系统提供了许多环境变量,它们定义了程序的执行环境,应用程序可以存取它们

Web Server(比如说Nginx)只是内容的分发者。比如,如果请求/index.html,那么Web Server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据。
好了,如果现在请求的是/index.php,根据配置文件,Nginx知道这个不是静态文件,需要去找PHP解析器来处理,那么他会把这个请求简单处理后交给PHP解析器。
Nginx会传哪些数据给PHP解析器呢?url要有吧,查询字符串也得有吧,POST数据也要有,HTTP header不能少吧,好的,CGI就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议

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

Web ServerCGI接口又另外设置了一些环境变量,用来向CGI程序传递一些重要的参数。CGI的GET方法还通过环境变量QUERY-STRING向CGI程序传递Form中的数据。

可以看一下Nginxfastcgi_params

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

##
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name

FastCGI

FastCGI是一种协议,它是在CGI标准协议基础上发展出来的一个变种协议,它的主要目标是减轻 Web ServerCGI 程序之间交互时的负载,这样一台服务器就可以在同一时间处理更多的 web 请求

FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute模式)。
它还支持分布式的运算,即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求

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

FastCGI的工作原理

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

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

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

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

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

运行示意图如下:

FAST CGI

FastCGI的不足

因为是多进程,所以比CGI多线程消耗更多的服务器内存,PHP-CGI解释器每进程消耗7至25兆内存,将这个数字乘以50或100就是很大的内存数

  • apache 模块方式

当PHP需要在Apache服务器下运行时,一般来说,它可以模块的形式集成,此时模块的作用是接收Apache传递过来的PHP文件请求,并处理这些请求, 然后将处理后的结果返回给Apache。如果我们在Apache启动前在其配置文件中配置好了PHP模块, PHP模块通过注册apache2的ap_hook_post_config钩子,在Apache启动的时候启动此模块以接受PHP文件的请求

PHP-CGI

PHP-CGI是PHP自带的解释器,PHP-CGI只是个CGI程序,他自己本身只能解析请求,返回结果,不会管理进程,所以就出现了一些能够调度PHP-CGI进程的程序,比如说由lighthttpd分离出来的spawn-fcgi

PHP-CGI变更php.ini配置后需重启PHP-CGI才能让新的php-ini生效,不可以平滑重启

直接杀死PHP-CGI进程,PHP就不能运行了。(PHP-FPMSpawn-FCGI就没有这个问题,守护进程会平滑从新生成新的子进程。)

PHP-CGI是PHP提供给Web Server也就是HTTP前端服务器的CGI协议接口程序,当每次接到HTTP前端服务器的请求都会开启一个PHP-CGI进程进行处理,而且开启的PHP-CGI的过程中会先要重载配置,数据结构以及初始化运行环境,如果更新了PHP配置,那么就需要重启PHP-CGI才能生效,例如phpstudy就是这种情况

php-cgi与php的区别

PHP-CGI与PHP的区别(在win下就是php-cgi.exe与php.exe)在于,php/php.exe是命令模式的php解释器,而php-cgi/php-cgi.exe是支持“通用网关接口”的php解释器,而通用网关接口就是我们前面说的CGI(从它的名称就能看出来啦,它都标明了“-cgi”了),不过现在的PHP-CGI是即支持“CGI”协议,也支持CGI的改进版——FastCGI

PHP-FPM

fpm是FastCGI Process Manager的缩写,PHP-FPM是一个PHP FastCGI管理器,是一个实现了FastCGI(协议)的程序,是只用于PHP的

对于 php5.3 之前的版本来说,PHP-FPM 是一个第三方的补丁包,旨在将 FastCGI 进程管理整合进PHP包中。在 php5.3 之后的版本中,PHP-FPM 不再是第三方的包,它已经被集成到 php 的源码中了,因为 PHP-FPM 提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载 PHP 配置,比 spawn-fcgi 具有更多优点,所以 PHP-FPM 被 PHP 官方集成了

PHP-FPM是PHP提供给Web Server也就是HTTP前端服务器的FastCGI协议接口程序,它不会像PHP-CGI一样每次连接都会重新开启一个进程,处理完请求又关闭这个进程,而是允许一个进程对多个连接进行处理,而不会立即关闭这个进程,而是会接着处理下一个连接。
它可以说是PHP-CGI的一个管理程序,是对PHP-CGI的改进

PHP-FPM会开启多个PHP-FPM程序,并且PHP-FPM常驻内存,每次Web Server服务器发送连接过来的时候,PHP-FPM将连接信息分配给下面其中的一个子程序PHP-CGI进行处理,处理完毕这个PHP-CGI并不会关闭,而是继续等待下一个连接,这也是FastCGI加速的原理,但是由于PHP-FPM是多进程的,而一个PHP-CGI基本消耗7-25M内存,因此如果连接过多就会导致内存消耗过大,引发一些问题,例如nginx里的502错误

PHP-FPM的管理对象是PHP-CGI,但不能说PHP-FPMFastCGI进程的管理器,因为前面说了FastCGI是个协议

注:
PHP-FPM是用于管理PHP-CGI的,这个说法对也不对,说他对,是因为在php5.4以前确实是这样的,但php5.4以后,PHP-FPM已被php官方收编,本身已经自带了解析php的功能,不只是做进程管理,也不再依赖PHP-CGI来解析php了

既然PHP-FPM也是“遵循通用网关接口的php解释器”,那么有一点可以确定的是,PHP-FPM不会调用PHP-CGI也不会依赖PHP-CGI,因为它本身就是PHP-CGI的改进版,没有理由去调用(去依赖PHP-CGI),确定方法很简单,先停掉你的PHP-FPM,然后把php-cgi改个名或者移动到另一个目录,再启动PHP-FPM,看一切是否正常?答案是肯定正常的,PHP-FPM根本不依赖php-cgi

对于php.ini文件的修改,PHP-CGI进程是没办法平滑重启的,有了PHP-FPM后,就把平滑重启成为了一种可能,PHP-FPM对此的处理机制是新的worker用新的配置,已经存在的worker处理完手上的活就可以歇着了,通过这种机制来平滑过度的。

Reference

https://novnan.github.io/PHP/CGI_FastCGI_php-cgi_php-fpm/
https://www.jianshu.com/p/80e46a80fdbd
https://www.cnblogs.com/fps2tao/p/10730461.html


文章作者: 江湖义气
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 江湖义气 !
  目录