PHP学习系列 -- CLI、CGI、FastCGI、php-fpm学习

在上一篇文章中我们了解到,Web Server与PHP之间通过sapi来实现解耦,那这个过程到底是什么呢?cli、cgi、fastcgi、php-fpm又都是什么呢?别急,在这篇文章中,我们会对其有一个清晰的了解。

首先,cli、cgi、fastcgi都是sapi的一种实现形式,是不是有点模糊?没问题,下面先对其概念有一个简单的了解。

CLI模式

CLI : Command Line Interface(命令行接口),通过命令行方式,我们无需通过Web Server即可执行PHP脚本;

CLI模式下,php.ini中的某些配置对其不起作用。如不受php脚本执行时间的限制(Web Server通常有响应时间的限制);

具体CLI模式下在不同平台下如何执行php脚本以及传递参数,请参考PHP cli模式

目前,CLI模式下执行php脚本的情况比较少,究其原因是无法满足复杂的业务需要,也不能传递post参数、上传文件,ui交互较差,更适合开发人员使用;

CGI

CGI:Common Gateway Interface(公共网关接口),是一种与语言无关的协议。

在进一步学习之前我们先来考虑一下这种情况:Web Server能够取到浏览器传递过来的get post参数,而Web应用程序也能接受stdin(标准输入),如此一来,将Web Server接受到的参数直接传给Web 应用程序不就OK了吗?

可事实上有这么简单吗?你想一下,两个人在做买卖,一个在说法语(Web Server),另一个人在说汉语(Web 应用程序),两个人之间如何进行交流?任何一方迁就另一方都不太可能。好在两方对英语还都掌握得OK,那好,那就定一个标准语言:英语,以后两方就通过英语进行沟通。这里的英语就类似于CGI,php-cgi就是该协议的一种实现方式。

一个动态请求到达Web Server之后,Web Server会根据cgi协议封装请求信息和环境变量组成stdin,通过Tcp链接或socket方式将stdin传递给php-cgi。 

OK,到目前为止,我们已经知道了请求经过Web Server转发之后最终到了php-cgi这里。那么,这个php-cgi又做了什么工作呢?和我们前面章节学习的Zend 引擎、词法、语法分析又有什么关系呢?

PHP学习系列 -- CLI、CGI、FastCGI、php-fpm学习

上面的内容可能部分不够准确,但大致流程是没问题的。

只不过,CGI模式也称为fork-execute-kill 模式:每当有一个请求过来时,Web Server都会启动一个php-cgi去处理这个请求,请求处理完成之后这个php-cgi就会自动销毁。对于QPS较小的情况下,CGI模式还好,但对于成百上千的QPS,这个时候的平响就会很长。为什么这么说呢?因为Web Server每创建一个php-cgi都是要给其分配内存和其他资源的,QPS较大时就会造成内存以及其他资源的紧张,最终造成整个平响的超长。

有一点要注意:CGI模式下,php-cgi的启动是受Web Server控制的。

对于目前很多高并发的网站而言,CGI模式很显眼不能满足他们的需求,那有没有什么办法解决一下呢?

回想一下上面php-cgi的产生、销毁过程:请求达到Web Server → 生成php-cgi进程 → 请求转发给php-cgi→php-cgi处理请求 → 返回给Web Server处理结果 → php-cgi 销毁。有没有一种方法可以实现php-cgi预生成(可能不够准确)、常驻内存呢?答案是肯定的,这也就是我们下面要说的fastcgi模式。

fastcgi

fastcgi也是一种协议,PHP语言的实现方式为php-cgi。fastcgi是cgi的升级版,既然是升级版,那较cgi又提升了哪些功能呢?

提升点就是php-cgi预生成与常驻内存。对于这两点大家可能不理解,解释一下。

预生成:在请求到达php-cgi之前就生成一定数量的php-cgi。

常驻内存:php-cgi在处理过一个请求之后并不会销毁,它会一直存在,等待着php-fpm分配的下一个请求。

OK,上面也说了,预生成php-cgi的时候会生成一定的数量。这些php-cgi在Web Server的某个请求转发过来之后都能对该请求进行处理,如果每个php-cgi都尝试进行处理就会造成"惊群效应"。那到底哪个能处理呢?很显然,我们需要对这些个php-cgi进行一个进程调度,php-fpm出现了。

php-fpm

前面说了,php-fpm是php-cgi的进程管理器。

这里有一点要注意,CGI协议时,php-cgi的启动是受Web Server控制的;fastcgi协议时,php-cgi的启动跟Web Server没有任何关系了,它只受php-fpm的调度。而且,这个时候Web Server转发请求以及传递参数给某个php-cgi都需先经过php-fpm的调度,之后再由php-fpm控制这个请求具体交给那个php-cgi处理。

可以将php-fpm独立运行在非web服务器上,实现所谓的动静分离。

盗用别人的一张图,下面是fasgcgi接口协议下一个客户端请求响应的完整过程。

PHP学习系列 -- CLI、CGI、FastCGI、php-fpm学习


备注:

上面我们说到了CGI存在的诟病,并提出了一种解决方案:预生成、常驻内存。除了fastcgi协议之外,其实还有另外一种方式。将php作为Apache的一个扩展编译进去,在Apache启动时加载并**php_module。这个时候,php-cgi常驻在httpd进程内部。当动态请求到达时,httpd不用再生成php-cgi,而是直接将动态请求转发给它内部php-cgi。但是,这也有一个问题:高QPS时性能会下降,因为这种方式生成的php-cgi只有一个。

CGI和fastcgi都只是协议。各种支持和WEB交互的编程语言对CGI/fastcgi协议都做了各自的实现(当然,任何一种语言都能写CGI脚本),而php上的php-cgi和php-fpm正是php对fastcgi协议的实现。

参考:PHP的命令行模式