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
程序通过标准输入(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 Server
和CGI
接口又另外设置了一些环境变量,用来向CGI
程序传递一些重要的参数。CGI
的GET方法还通过环境变量QUERY-STRING向CGI
程序传递Form中的数据。
可以看一下Nginx
的fastcgi_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 Server
与CGI
程序之间交互时的负载,这样一台服务器就可以在同一时间处理更多的 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 server
将CGI
环境变量和标准输入发送到FastCGI
子进程PHP-CGI
FastCGI
子进程完成处理后将标准输出和错误信息从同一连接返回Web Server
。当FastCGI
子进程关闭连接时,请求便告处理完成。FastCGI
子进程接着等待并处理来自FastCGI
进程管理器(运行在Web Server
中)的下一个连接。 在CGI
模式中,PHP-CGI
在此便退出了
在上述情况中,你可以想象CGI
通常有多慢。每一个Web请求PHP都必须重新解析php.ini
、重新载入全部扩展并重初始化全部数据结构。使用FastCGI
,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作
运行示意图如下:
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-FPM
和Spawn-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.ex
e与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-FPM
是FastCGI
进程的管理器,因为前面说了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