CGI原理示例,及CGI,FastCGI,php-cgi,php-fpm等的总结

CGI

CGI全名“通用网关接口”(Common Gateway Interface),是一个技术规范,用来动态生成网页html。理论上可以使用任意语言写,只要支持标准输入输出及环境变量即可(标准输入输出概念参考C语言中stdio库的printf函数)。

举例简述一下实现细节,以类C语言伪代码演示(不想了解CGI细节可以跳过)

CGI程序 /usr/local/cgi/hello

printf("Content-type: text/html; charset=UTF-8")
printf("\r\n\r\n")
printf("hello world")

注意两个连续换行符号,它们是http头与内容的分隔。

假设web服务器,把对 http://localhost/hello的请求,转给 /usr/local/cgi/hello 处理,这就要启用一个进程支行hello程序,hello程序执行后标准输出结果如下

Content-type: text/html; charset=UTF-8

hello world

web服务器拿到结果,把并把hello程序的输出结果添加其它http头元素,如HTTP状态码、Connection: keep-alive等,这就是完整的http响应消息内容了,把它们一并发回给浏览器,一次CGI的http请求就完成。

这是个最简单的示例,我们忽略了http请求数据、本地目录文件等,事实上的程序会复杂得多。

通常是这样的,一个cgi-bin目录里,放置CGI可执行程序(类比上面的hello程序),web服务器通常不是把所有请求都通过CGI来处理的,而是把符合一定规则(比如url路径以.pl结尾)的请求,启动CGI进程,并将对应的web目录文件(比如hello.pl)传给CGI程序,CGI程序按hello.pl文件的内容执行,标准输出结果再由web服务器拿到并补充http头等,生成http影响数据,发回给浏览器。

这里的CGI程序的行为,与脚本语言解释器类似(本地目录的相关文件当脚本程序,被CGI程序解释执行)

CGI的具体细节参考rfc3875,或 英文维基CGI词条

FastCGI

传统的CGI程序,对每次http请求,都要启动一个操作系统层面的进程process,进程处理一次就结束。下次http请求还得再起动。启动进程对于操作系统来说是较耗费时间的操作,所以在CGI的负载能力比较有限,在启动关闭进程上浪费严重性能。

针对这一点,FastCGI协议被出了:让CGI进程长期驻守,有请求,就直接给它处理,没有就等待。

可以这样认为,FastCGI是CGI的改进版,虽然并不完全兼容。传统的CGI要改造成FastCGI,很多代码可以直接借用的。

因为CGI/FastCGI协议都是语言无关的,只要能按这个规范来,任何语言都可以。所以有很多具体的技术支持,比如php的CGI/FastCGI模式运行;这点后面讨论。

单个FastCGI进程的负载是有限的,所以通常需要多个进程同时工作,这就可以同时接受更多请求,提高并发数。

如果某个进程运行时间长了,占用了过多内存,或者该进程僵死了,需要结束它,并启动新进程;或者想在并发量大时,自动增加FastCGI进程数,而并发小时,自动减少;...这些就需要有有“人”来管理,就是FastCGI进程管理员的工作了。

php-cgi

编译后php的二进制文件里,在bin目录里有php-cgi文件(在windows版里是,php-cgi.exe),即是支持CGI/FastCGI的可执行程序。

可以这样执行一个php文件  /path/to/php-cgi /path/to/your-php-file.php

php-fpm

前面说过,FastCGI程序是驻守系统进程中的,该进程不可能凭空启动,那这个进程由谁管理呢?这就是FastCGI进程管理员 (FastCGI Process Manager)。对于PHP而言,在php 5.3以后的版本,自带的php-fpm即是php的fpm。(php 5.2及以前版本,可以通过补丁的形式将php-fpm整合到php中,或使用其它方案)。

不过,支持php的fpm并非只有php-fpm,还有其它方案,只要起到管理fastCGI进程的启动、关闭等 功能即可。apache下的mod_fcgid即是另一个可选方案。所以,在apache下,如果使用mod_fcgid整合php,就不需要php-fpm了。有更广泛支持的Spawn-FCGI是另一个备选项。