缘起
关于web开发的服务器端的编程的技术,个人了解的有ASP,JSP,PHP等系列的脚本语言。ASP使用IIS,JSP使用Tomcat或Apache,PHP之类的脚本语言使用Apache或CGI。这里关于CGI的认识有点模糊,尤其是决定学习Ruby和Rails时,频频能看到CGI这个词。十分好奇,CGI到底为何物,搜索了一番。
1. CGI
1.1. 历史
最初,CGI是在1993年由美国国家超级电脑应用中心(NCSA)为 NCSA HTTPd Web 服务器(http和sqlite一样,都是公公域软件,针对httpd补丁的汇总构成了最初的Apache)开发的。Httpd Web服务器使用了UNIX shell 环境变量来保存从 Web 服务器传递给CGI的参数,然后生成一个运行 CGI 的独立的进程。
1.2. 简介
公共网关接口CGI(Common Gateway Interface)是WWW技术中最重要的技术之一,其规范的地址为
http://www.ietf.org/rfc/rfc3875。CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规程。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器。CGI运行在网络服务器上。CGI程序可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量,如php,perl,tcl等。
大多数CGI程序用来处理和解释来自表单的输入信息,并在服务器进行相应的处理,或将相应的信息反馈给浏览器。CGI程序使网页具有交互功能。
1.3. CGI处理流程
简单来说:CGI可以理解为一个很简单的远程脚本调用。在服务器端有许多的脚本,这些脚本可以使用任何可执行语言编写(shell、ruby、perl、PHP),然后用户通过url访问web服务器,服务器会根据已有的url mapping找到对应的一个脚本,然后执行这个脚本。最后把脚本执行的结果按照既定的格式(比如Html,xml,json之类的)返回给用户。
CGI的简单处理流程:1.用户点击URL发送请求给web服务器 2.web服务器接受请求转交CGI程序 3.CGI程序把处理结果传送给web服务器 4.web服务器把结果发给到用户。这个处理流程就是著名的请求/响应模型,web开发都要遵守这个模型。
上述步骤中web服务器接受请求转交CGI程序,需要配置web服务器,将对因的URL请求映射为CGI程序(大多为脚本程序)。
每次CGI请求都要生成一个进程来运行,大量请求就会压垮服务器。对脚本语言而言,每次请求都重新载入和初始化解释器,开销太大。此时,可以将脚本语言直接集成到web服务器中(例如,Apache中的mod_perl,mod_php,mod_python,mod_ruby)。CGI相关的Apache模块:mod_actions(CGI脚本类型),mod_cgi(启动CGI并维护相关事件出错日志),mod_cgid,mod_env(设置CGI编译器的环境变量,比如软件库)
1.4. CGI环境变量
CGI实际通过Shell环境变量来获取相应的参数,一些常用的环境变量如下:
服务器类变量:
SERVER_NAME:运行CGI序为机器名或IP地址。
GETWAY_INTERFACE:CGI程序的版本,在UNIX下为 CGI/1.1
SERVER_INTERFACE:WWW服务器的类型,如:CERN型或NCSA型。
请求类变量:
SERVER_PROTOCOL:通信协议,应当是HTTP/1.0。
SERVER_PORT:TCP端口,一般说来web端口是80
REQUEST_METHOD:HTTP方法名,常用的有GET,PUT,DELETE,POST
PATH_INFO:浏览器用GET方式发送数据时的附加路径前缀
PATH_TRANSLATED:根据PATH_INFO指定的服务器上实际路径名。
SCRIPT_NAME:CGI程序的相对路径名,比如/cgi-bin/script.cgi
QUERY_STRING:URL中问号后的内容,通过GET方法提交的表单输入的数据,格式为name=value,以&分隔
REMOTE_HOST:客户端的主机名,不能确定该值。
REMOTE_ADDR:客户端的IP地址。
REMOTE_USER:客户端的人名,用作某些权限类型
CONTENT_TYPE:使用PUT或POST发送的因特网媒体数据,通过HTTP包头提供
CONTENT_LENGTH:POST或PUT提交的数据的大小,由HTTP包头提供
HTTP_ACCEPT, HTTP_ACCEPT_LANGUAGE, HTTP_USER_AGENT, HTTP_COOKIE以及其他相关的变量包含HTTP包头中对应的数据。
HTTP_ACCEPT:HTTP定义的浏览器能够接受的数据类型。
HTTP_REFERER:发送表单的文件URL。(并非所有的浏览器都传送这一变量)
HTTP_USER_AGENT:发送表单的浏览器的有关信息。
CGI部署的约定:
1.cgi-bin目录作为所有CGI程序的根目录
2.扩展名约定:.cgi的文件为CGI脚本。
1.5. CGI编程语言
CGI本身独立于任何编程语言,只要相应的语言可以运行在服务器上,CGI 程序可以用任何脚本语言或者是完全独立编程语言实现。除 Perl 外,Unix shell script, Python, Ruby, PHP, Tcl, C/C++, 和 Visual Basic 都可以用来编写 CGI 程序,但Perl被广泛用来编写CGI程序。
脚本语言灵活性和高效开发效率,可以快速开发CGI程序,同时可以完成非常多的任务。CGI中脚本语言介绍如下:
Perl
1987年,Larry Wall吸取C,sed,awk,内部集成正则表达式,瑞士军刀般的脚本语言。
PHP
1994年,Rasmus Lerdorf,个人网站开发,Perl程序,C重写,Zend Engine,php5。
特点:
1.混合C,java,Perl,加自创语法
2.在执行动态网页上,比其他动态语言(Perl)速度快
3.拥有强大的功能,可替代CGI
4.支持主流数据库和操作系统
5.可以用C/C++扩展
优势:开源,免费,快捷,夸平台,高效,面向对象,图像处理
Ruby
1995年,吸收了Smalltalk,Perl,Lisp等语言,是一个让程序员感到快乐的语言。
其他的就不介绍了,诸如Python这类的语言。
1.6. 进一步学习
CGI的执行程序的模式为fork & execute,即对每次请求都创建一个新的进程(fork系统调用),然后执行,这种方法容易实现,但效率差,难以扩展,面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。因而存在替代性的解决方案:
1.web服务器自己实现允许第三方软件在其中运行的扩展机制,比如Apache的扩展模块,NSAPI插件和ISAPI插件
2.FastCGI
3.Simple Common Gateway Interface 或SCGI
4.使用其他的动态网站架构,比如Java EE,在Java servlet容器中运行Java代码提供动态或静态内容,通过多线程来取代多进程。
CGI标准:http://www.w3.org/CGI/
一些免费的CGI书籍:
2.http://oreilly.com/openbook/cgi/
2. CGI变体
由于CGI的fork&execute模型存在性能问题,因而存在一些CGI变体。罗列如下:
2.1. FastCGI
FastCGI官方地址:http://www.fastcgi.com/drupal/ FastCGI是CGI的增强版,致力于减少web服务器和CGI程序之间互动开销,从而使服务器可以同时处理更多的网页请求。
CGI
与CGI中为每个请求创建新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI服务器管理,而不是web服务器。当请求到来时,web服务器把环境变量和页面请求通过一个socket(FastCGI进程在服务器上)或者TCP connection(FastCGI进程在远程服务器上)传递给FastCGI进程。
FastCGI像是一个常驻(long-live)型的CGI,可以一直运行,一旦激活后,不必每次都要花费时间去fork进程(这是CGI最为人诟病的fork-and-execute 模式)。支持分布式的运算,即FastCGI程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。
在Apache中,FastCGI通过mod_fcgid模块或mod_fastcgi模块实现。
2.1.1. FastCGI处理流程
FastCGI处理的流程如下:
1、Web服务器启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)
2、FastCGI进程管理器自身初始化,启动多个CGI解释器进程(比如多个php-cgi)并等待来自Web服务器的连接
3、当客户端请求到达Web服务器时,FastCGI进程管理器选择并连接到一个CGI解释器。Web服务器将CGI环境变量和标准输入发送到FastCGI子进程(比如php-cgi)
4、FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web服务器。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web服务器中)的下一个连接。在CGI模式中,php-cgi在此便退出了
在上述情况中,使用CGI,每一个PHP页面请求都必须重新解析php.ini、重新载入全部扩展并重初始化全部数据结构。使用FastCGI,所有这些都只在进程启动时发生一次。额外的好处,可以使用持续数据库连接(Persistent database connection)。
2.1.2. 特点
优点:语言无关,独立安全的执行环境,支持诸多语言和服务器,独立web服务器内部架构
缺点:多进程,耗内存
2.1.3. 进一步学习
FastCGI官方网:http://www.fastcgi.com/drupal/ 中的文档。
2.2. SCGI-Simple Common Gateway Interface
Simple Common Gateway Interface (SCGI)是一个和HTTP Server交互的应用程序接口,作为CGI的替代选项。和FastCGI类似,但更易实现,且部分运行CGI操作连接到外部数据库。
2.2.1. 规范Specification
客户端通过允许传输8位的可靠流控制协议来连接到SCGI服务器。客户端通过发送请求开始,但SCGI服务器看到请求结束后,返回一个相应并关闭连接。请求格式如下,相应的格式在规范中没有定义。
请求格式
请求由一组头部和一个主体(body)组成,头部的格式如下:
headers ::= header* header ::= name NUL value NUL name ::= notnull+ value ::= notnull* notnull ::= <01> | <02> | <03> | … | <ff> NUL = <00> |
头部不允许出现重复字符串,第一个头部的name必须为CONTENT_LENGTH,value为body的长度。即使value为0,也必须有要CONTENT_LENGTH和SCGI这两个头部,为了操作CGI转换,标准CGI的环境变量也必须在头部提供。
头部编码为网络字符串(netstring即[len]":"[string]","格式)并发送给服务器应用程序,body随后发送,其长度由CONTENT_LENGTH头指定。
样例
web 服务器(SCGI客户端)打开一个连接并发送如下字符串连接:
"70:" "CONTENT_LENGTH" <00> "56" <00> "SCGI" <00> "1" <00> "REQUEST_METHOD" <00> "POST" <00> "REQUEST_URI" <00> "/deepthought" <00> "," "What is the answer to life, the Universe and everything?" |
Web应用程序(SCGI 服务器)发送如下相应结束关闭链接:
"Status: 200 OK" <0d 0a> "Content-Type: text/plain" <0d 0a> "" <0d 0a> "42" |
2.2.2. 实现
服务器实现:Apache Http Server的SCGI模块、Lighttpd、Nginx、IIS的ISAPI SCGI扩展
语言接口:Haskell, Java, Lisp, Perl,PHP, Python, Ruby, Tcl
2.2.3. 进一步学习
SCGI规范:http://www.python.ca/scgi/protocol.txt
Apache SCGI modules and Python SCGI interface
3. PSGI
Perl web server Gateway Interface,简称PSGI,是web服务器和基于Perl的web应用程序和框架的接口,用来编写可移植的应用程序(单独运行或使用CGI,FastCGI,mod_perl等)。项目灵感来自Python的WSGI(Web Server Gateway Interface),Ruby的Rack,JavaScript的JSGI。
PSGI的应用程序是Perl子程序,它接受单个哈希表参数,返回包含三个元素的数组(HTTP状态码,HTTP头数组的引用,HTTP body行数组的引用)。其中HTTP body用来生成HTML文档。
Plack是PSGI的一个实现,其支持的Perl框架很多(参见参考资料8)。
3.1. 样例
PSGI的hello world程序hello.psgi:
my $app = sub {
return [200, ['Content-Type' => 'text/plain'], ["hello, world\n"]];
}
运行命令:plackup hello.psgi
3.2. 进一步学习
PSGI规范:Perl Web Server Gateway Interface Specification
Catalyzed上关于Plack和Nginx的文章:http://www.catalyzed.org/2009/11/mtplack-on-nginx-love.html
4. WSGI
Web Server Gateway Interface,中文名Web服务器网管接口,简称WSGI,是为Python定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口(比如Perl中的PSGI,Ruby中的Rack)。
4.1. 缘由
以前,如何选择Python框架是一个问题。Web框架的选择会限制web服务器的选择,反之亦然。WSGI出现之前,Python应用程序通常为CGI,FastCGI,mod_python甚至特定web服务器设计的。
WSGI是服务器(web服务器)与应用程序之间的一个低级别接口,提供开发可移植web应用。WSGI是基于CGI标准的,所以可以套接到CGI上。
简单来看,WSGI就是服务器与应用程序之间的中间件,他对于服务器来说,就是应用程序,对于应用程序来说就是服务器,在中间做中转处理,比如,在request和response上做修改。
4.2. 规范概览
WSGI有两方:“服务器”或“网关”一方,以及“应用程序”或“应用框架”一方。服务方调用应用方,提供环境信息,以及一个回调函数(提供给应用程序用来将消息头传递给服务器方),并接收Web内容作为返回值。
WSGI中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起调解作用:从WSGI服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。“中间件”组件可以执行以下功能:
- l 根据目标URL,将请求消息路由到不同的应用对象。
- l 允许在一个进程中同时运行多个应用程序或应用框架。
- l 负载均衡和远程处理,通过在网络上转发请求和响应消息。
- l 进行内容后处理,例如应用XSLT样式表。
4.3. 实例
Python语言写的符合WSGI的“Hello World”应用程序如下所示:
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield "Hello world!\n"
代码解释:
第一行定义了一个名为app的callable,接受两个参数,environ和start_response,environ是包含了CGI中的环境变量的字典,start_response也是一个callable,接受两个必须的参数,status(HTTP状态)和response_headers(响应消息的头)。
第二行调用了start_response,状态指定为“200 OK”,消息头指定为内容类型是“text/plain”
第三行将响应消息的消息体返回
4.4. 进一步学习
WSGI维基百科:.http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface
PEP 3333 -- Python Web Server Gateway Interface v1.0.1
WSGI metaframework啄木鸟关于wsgi的介绍
5. Rack
Rack提供最小的,模块化的,可配性接口来开发Ruby的web应用程序。通过以最简方法包装HTTP请求和响应,it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.
Rack已经被用到所有的Ruby web框架和库,比如Ruby on Rails和Sinatra。可以通过Ruby Gem进行安装。
5.1. 简介
Rack就是Ruby的WSGI,是在服务器与应用程序提供一个中间件。通过截获用户发来的请求和应用程序以既定形式返回的数据,然后作出相应的处理。例如,一个用户的请求到来后,验证用户的访问url是否有效,或者验证用户的身份等。
作为一个中间件,rack的主要作用体现在通用逻辑和实际业务逻辑的分离,在rack中加入通用的逻辑,而无需在实际业务程序中添加类似的功能。由于rack标准的普遍性,几乎所有的主流Ruby web框架(Ruby on Rails和Sinatra)都按照rack标准开发的模块,rack也为开发者提供了大量的api来简化开发。
5.2. 样例
Rack兼容的Hello world程序:
app = lambda do |env|
body = "Hello, World!"
[200, {"Content-Type" => "text/plain", "Content-Length" => body.length.to_s}, [body]]
end
run app
5.3. 进一步学习
Rack编程中文pdf文档:http://www.iteye.com/topic/605707
后记
写博客最重要的是确定主题,有了主题,可以收集资料,分析处理资料,整理成文。主题(topic)的来源有很多,看到的,听说的,想到的都可以衍生出主题。
写博客要明确读者,自己或是别人,要站在读者角度,多问自己几个问题,想说什么,怎么说,有用吗,有趣吗。
CGI程序没有开发过,但是,自己曾经安装过一个使用CGI的Perl程序-Monitorix。写这篇博客不容易啊,前后花了两天的时候。不过,也了解了很多有趣的东西,CGI,Libwww,Rack,其实我最感兴趣的是
参考文献
1.CGI,WSGI和Rack:http://blog.csdn.net/cherry_sun/article/details/7751452
2.CGI 百度百科:http://baike.baidu.com/subview/32614/12037322.htm
3.Wikipedia:公共网关接口
4.Common Gateway Interface:http://en.wikipedia.org/wiki/Common_Gateway_Interface
5.Fast CGI百度百科:http://baike.baidu.com/view/641394.htm
6.Fast CGI 维基百科: http://zh.wikipedia.org/wiki/FastCGI
7.Simple Common Gateway Interface:http://en.wikipedia.org/wiki/Simple_Common_Gateway_Interface
8.PSGI:http://en.wikipedia.org/wiki/PSGI
9.WSGI中文维基