一、动态网站与CGI技术?
在Web发展初期,只有静态页面处理能力。比如我们所熟知的Apache、Nginx服务器都是web服务器,它们有一个共同特点就是只能处理静态请求。也就是说,只能把本地的现成的html,css之类的东西传给浏览器,然后经过浏览器渲染成一个web页面。事物总是不断发展,网站也越来越复杂,所以出现动态技术,也就是我们今天所说的动态网站技术。而动态网站的成功是离不开CGI(Common Gateway Interface)公共网关接口技术的出现。
CGI(Common Gateway Interface)公共网关接口是WWW技术中最重要的技术之一,有着不可替代的重要地位。CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规范。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完整的新的交互式媒体。通俗的讲CGI就像是一座桥,把网页和WEB服务器中的执行程序连接起来,它把HTML接收的指令传递给服务器的执行程序,再把服务器执行程序的结果返还给HTML页。CGI程序使网页具有交互功能,但需跟web服务在同一台主机上面运行,不能够夸主机通信。CGI的跨平台性能极佳,几乎可以在任何操作系统上实现。
CGI之所以称之为通用网关接口,主要特点就在于CGI程序可以是Python脚本,PERL脚本,SHELL脚本,C或者C++程序等。
那么我们看一个实际例子:现在的个人主页上大部分都有一个留言本。留言本的工作是这样的:先由用户在客户端输入一些信息,如名字之类的东西。接着用户按一下“留言”(到目前为止工作都在客户端),浏览器把这些信息传送到服务器的CGI目录下特定的CGI程序中,于是CGI程序在服务器上按照预定的方法进行处理。在本例中就是把用户提交的信息存入指定的文件中,然后CGI程序给客户端发送一个信息,表示请求的任务已经结束。此时用户在浏览器里将看到“留言结束”的字样,整个过程结束。
大概工作流程图如下:
通俗点解释,CGI处理流程如下图:
CGI模式在处理用户请求时,机制如下:
1. 每当客户请求CGI程序的时候,WEB服务器就请求操作系统生成一个新的CGI解释器进程,CGI的一个进程则处理完一个请求后退出,下一个请求来时再创建新进程,这也是CGI最为人诟病的fork-and-execute模式。
2. CGI方式的服务器上有多少请求就会有多少CGI子进程,每个子进程都需要启动CGI解释器,加载配置等初始化操作。这也是CGI性能低下的主要原因,当用户请求稍微一多,会大量挤占系统资源,如内存、CPU等。
由于CGI不安全以及性能极其低下的问题,由此衍生了很多其他分支,比如FastCGI(PHP语言使用)。比如ISAPI(Internet Server Application Program Interface)是微软提供的一套面向WEB服务的API接口,它能实现CGI提供的全部功能,并在此基础上进行了扩展。还有比如Python语言使用的WSGI等。现在的CGI基本没人使用,都是使用各自语言基于CGI标准衍生的更优的网关接口。另外多数流行的Web Server都支持FastCGI,WSGI;包括Apache、Nginx和lighttpd等。
有了CGI技术后,如HTML+PHP组成的动态网站,用户如果访问的是静态内容则直接由web服务器返回。如果访问的是动态内容则由web服务器根据CGI技术开启一个进程交由能够处理PHP的程序来运行,然后返回给用户。但是这种执行后的数据是没有格式和编排的,无法格式化成一级标题和链接等,那么在显示给浏览器时,这些样式从哪里来呢?在CGI技术上它没有办法提供说显示的标签是由web服务器提供而执行的数据和处理结果是由PHP程序提供,所以所有的程序都只能由后端的PHP程序来生成。由此HTML文档只能通过PHP程序员给它echo或print出来才能加以显示(当然PHP程序不需要这样,因为PHP足够先进可以自己直接处理它的显示了)。这样一来,当HTML发生改变时整个源程序都需要重新编译一次(动态内容发生改变时必须要重新编译的),我们缓存下来的opcode都有可能失效,所以这就后来出现了MVC框架将展示、数据、业务分来处理;将PHP直接嵌入到HTML中这样一样HTML就不需要让语言本身来处理了。
二、FastCGI的改进?
FastCGI是从CGI发展改进而来的,属于CGI协议的一个分支,也是一个标准协议。它的核心思想就是在web服务器和具体CGI程序之间建立一个智能的可持续的中间层,统管CGI程序的运行。
FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。众所周知,CGI解释器的反复加载是CGI性能低下的主要原因,如果CGI解释器保持在内存中并接受FastCGI进程管理器调度,则可以提供良好的性能、伸缩性、Fail-Over特性等等。当Web服务器将请求提交给这个层,这个层再派生出几个可复用的CGI程序实例,然后再把请求分发给这些实例。这些实例是可控的,可持续,可复用的, 因此一方面避免了进程反复fork,另一方面又可以通过中间层的控制和探测机制来监视这些实例的运行情况,根据不同的状况fork或者回收实例,达到灵活性和稳定性兼得的目的。
FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。
一般情况下,FastCGI的整个工作流程是这样的:
1. Web服务器启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module或Ningx Fastcgi)。
2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程(如果使用php-fpm管理器可见多个php-fpm进程)并等待来自Web服务器的连接。
3. 当客户端请求到达Web服务器时,Web FastCGI进程管理器选择并通过socket连接到一个CGI解释器,Web服务器将CGI环境变量和标准输入发送到FastCGI子进程php-fpm进程。
4. FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web服务器。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web服务器中)的下一个连接。而在CGI模式中,php-cgi进程在此便退出了。
在上述情况中,你可以想象CGI通常有多慢。每一个Web请求PHP都必须重新解析php.ini、重新载入全部扩展并重初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。
多数流行的Web server都支持FastCGI,包括Apache、Nginx和lighttpd等。同时,FastCGI也被许多脚本语言所支持,其中就有PHP、Python等。Web服务器利用FastCGI技术就可以跟PHP进行交互,在PHP端也提供了一个程序用于接收Web请求,称之为PHP-FPM,默认监听的是9000端口,在PHP5以上版本默认使用。
三、PHP-FPM介绍
Fastcgi是一个协议,PHP-FPM实现了这个协议。PHP-FPM是一个PHP FastCGI管理器,是只用于PHP的。PHP-FPM是一个完全独立的程序,不依赖php-cgi,也不依赖php。因为PHP-FPM是一个内置了php解释器的FastCGI服务,启动时能够自行读取php.ini配置和php-fpm.conf配置。
PHP-FPM(FastCGI Process Manager:FastCGI进程管理器)对于PHP 5.3.3之前的PHP来说,是一个补丁包 ,旨在将FastCGI进程管理整合进PHP包中。如果你使用的是PHP5.3.3之前的PHP的话,就必须将它patch到你的PHP源代码中,在编译安装PHP后才可以使用。
从PHP 5.4 RC2开始,PHP-FPM已经转正了,不再被PHP团队标注为EXPERIMENTAL(实验性的东西)。相对Spawn-FCGI(也是FastCGI的一个进程管理器),PHP-FPM在CPU和内存方面的控制都更胜一筹,而且前者很容易崩溃,必须用crontab进行监控,而PHP-FPM则没有这种烦恼。PHP5.3.3已经开始集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载PHP配置,比spawn-fcgi具有更多优点,所以被PHP官方收录了。在./configure的时候带–enable-fpm参数即可开启PHP-FPM。
PHP-FPM的使用非常方便,配置都是在php-fpm.ini的文件内,而启动、重启都可以从php/sbin/php-fpm中进行。更方便的是修改php.ini后可以直接使用php-fpm reload进行加载,无需杀掉进程就可以完成php.ini的修改加载。结果显示使用PHP-FPM可以使php有不小的性能提升。PHP-FPM控制的进程cpu回收的速度比较慢,内存分配的很均匀。
PHP-FPM功能包括:
1. 支持平滑停止/启动的高级进程管理功能;
2. 可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置);
3. stdout 和 stderr 日志记录;
4. 在发生意外情况的时候能够重新启动并缓存被破坏的 opcode;
5. 文件上传优化支持;
6. “慢日志” – 记录脚本(不仅记录文件名,还记录PHP backtrace信息,可以使用ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;
7. fastcgi_finish_request() – 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);
8. 动态/静态子进程产生;
9. 基本SAPI运行状态信息(类似Apache的 mod_status);
10. 基于php.ini的配置文件。
使用PHP-FPM来控制PHP-CGI的FastCGI进程。
1
2
3
4
5
6
7
|
$ /usr/local/php/sbin/php-fpm {start|stop|quit|restart|reload|logrotate}
start #启动php的fastcgi进程;
stop #强制终止php的fastcgi进程;
quit #平滑终止php的fastcgi进程;
restart #重启php的fastcgi进程;
reload #重新平滑加载php的php.ini;
logrotate #重新启用log文件;
|
四、总结
CGI是通用网关接口,是外部应用程序与Web服务器之间的接口标准,CGI是为了保证web server传递过来的数据是标准格式的,方便CGI程序的编写者。而FastCGI像是一个常驻(long-live)型的CGI,是用来提高CGI程序性能的。
CGI是个协议,跟进程什么的没关系。那Fastcgi又是什么呢?Fastcgi是用来提高CGI程序性能的。
提高性能,那么CGI程序的性能问题在哪呢?”PHP解析器会解析php.ini文件,初始化执行环境”,就是这里了。标准的CGI对每个请求都会执行这些步骤(不闲累啊!启动进程很累的说!),所以处理每个时间的时间会比较长。这明显不合理嘛!那么Fastcgi是怎么做的呢?首先,Fastcgi会先启一个master,解析配置文件,初始化执行环境,然后再启动多个worker。当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率自然是高。而且当worker不够用时,master可以根据配置预先启动几个worker等着;当然空闲worker太多时,也会停掉一些,这样就提高了性能,也节约了资源。这就是fastcgi的对进程的管理。
那PHP-FPM又是什么呢?是一个实现了Fastcgi的程序,被PHP官方收了。
大家都知道,PHP的解释器是php-cgi。php-cgi只是个CGI程序,他自己本身只能解析请求,返回结果,不会进程管理。所以就出现了一些能够调度php-cgi进程的程序,比如说由lighthttpd分离出来的spawn-fcgi。对,PHP-FPM也是这么个东东,在长时间的发展后,逐渐得到了大家的认可,也越来越流行。
Fastcgi是一个协议,PHP-FPM实现了这个协议。PHP-FPM的管理对象是php-cgi进程,但不能说PHP-FPM是Fastcgi进程的管理器,因为前面说了Fastcgi是个协议。
web server(比如说nginx)只是内容的分发者。比如,如果请求/index.html,那么web server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据。好了,如果现在请求的是/index.php,根据配置文件,nginx知道这个不是静态文件(需要在nginx.conf中增加php的配置:将php脚本转发到fastCGI进程监听的IP地址和端口,php-fpm.conf中指定),需要去找PHP解析器来处理,那么他会把这个请求简单处理后交给PHP解析器。Nginx会传哪些数据给PHP解析器呢?url要有吧,查询字符串也得有吧,POST数据也要有,HTTP header不能少吧,好的,CGI就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议。
<参考>
Python CGI编程
网关协议学习:CGI、FastCGI、WSGI
CGI,FastCGI,PHP-CGI与PHP-FPM
mod_php、mod_fastcgi和php-fpm的介绍,对比,和性能数据