CGI FastCGI WSGI 解析

我们将服务端程序分为了web服务器和应用程序服务器。
web服务器是用于处理HTML文件,让客户可以通过浏览器进行访问。主流的有apache,IIS,nginx,lghttpd等。
应用服务器处理业务逻辑,比如使用python的django,flask写成的程序
通常来自客户端浏览器的请求被web服务器截获,如果是静态请求,则如nginx会自己做处理,如果是动态请求,则会抛给后端应用服务器来处理。于是如何在web服务器与应用服务器之间进行通信成了主要问题,这就引出了以下三种处理的接口:CGI,FastCGI,WSGI。

1.CGI

通用网关接口Common Gateway Interface/CGI描述了客户端和服务器程序之间传输数据的一种标准,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。CGI 独立于任何语言的,CGI 程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。Unix
shell
 script, PythonRubyPHP,
perl, TclC/C++, 和 Visual
Basic
 都可以用来编写 CGI 程序。(http://www.dwz.cn/yFFgQ)

最初,CGI 是在 1993 年由美国国家超级电脑应用中心(NCSA)为 NCSA
HTTPd Web 服务器开发的。这个 Web 服务器使用了 UNIX shell 环境变量 来保存从 Web 服务器传递出去的参数,然后生成一个运行 CGI 的独立的进程。cgi的处理流程如下图所示:

CGI  FastCGI WSGI 解析

l   step1. web 服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量标准输入传递数据

l   step2. cgi进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等

l   step3. cgi程将处理结果通过标准输出标准错误,传递给web 服务器

l   step4. web 服务器收到cgi返回的结果,构建Http Response返回给客户端,并杀死cgi进程

web服务器与cgi通过环境变量、标准输入、标准输出、标准错误互相传递数据。

总结:CGI使外部程序与Web服务器之间交互成为可能。CGI程式运行在独立的进程中,并对每个Web请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。

2.FastCGI

快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。FastCGI致力于减少Web服务器CGI程式之间互动的开销,从而使服务器可以同时处理更多的Web请求。与为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI进程管理器管理,而不是web服务器。(http://www.dwz.cn/yFMap)

CGI  FastCGI WSGI 解析

当进来一个请求时,Web 服务器把环境变量和这个页面请求通过一个unix domain socket(都位于同一物理服务器)或者一个IP Socket(FastCGI部署在其它物理服务器)传递给FastCGI进程。

CGI  FastCGI WSGI 解析

l  step1. Web 服务器启动时载入初始化FastCGI执行环境 。 例如IIS ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi

l  step2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web 服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX 域socket两种方式启动。

l  step3. 当客户端请求到达Web 服务器时, Web 服务器将请求采用socket方式转发到 FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。

l  step4. FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web 服务器。当FastCGI子进程关闭连接时,请求便处理完成。

l  step5. FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。

由于 FastCGI 程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比CGI 技术提高 5 倍以上。它还支持分布式的部署, 即 FastCGI 程序可以在web 服务器以外的主机上执行。

总结:CGI 就是所谓的短生存期应用程序,FastCGI 就是所谓的长生存期应用程序。FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。

3.WSGI

Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口。WSGI是作为Web服务器与Web应用程序或应用框架之间的一种低级别的接口,以提升可移植Web应用开发的共同点。WSGI是基于现存的CGI标准而设计的。

WSGI区分为两个部份:一为“服务器”或“网关”,另一为“应用程序”或“应用框架”。在处理一个WSGI请求时,服务器会为应用程序提供环境资讯及一个回呼函数(Callback Function)。当应用程序完成处理请求后,透过前述的回呼函数,将结果回传给服务器。所谓的 WSGI 中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起调解作用:从WSGI服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。“中间件”组件可以执行以下功能:

  1. 重写环境变量后,根据目标URL,将请求消息路由到不同的应用对象。
  2. 允许在一个进程中同时运行多个应用程序或应用框架。
  3. 负载均衡和远程处理,通过在网络上转发请求和响应消息。
  4. 进行内容后处理,例如应用XSLT样式表。

以前,如何选择合适的Web应用程序框架成为困扰Python初学者的一个问题,这是因为,一般而言,Web应用框架的选择将限制可用的Web服务器的选择,反之亦然。那时的Python应用程序通常是为CGI,FastCGI,mod_python中的一个而设计,甚至是为特定Web服务器的自定义的API接口而设计的。WSGI没有官方的实现, 因为WSGI更像一个协议。只要遵照这些协议,WSGI应用(Application)都可以在任何服务器(Server)上运行,
反之亦然。WSGI就是Python的CGI包装,相对于Fastcgi是PHP的CGI包装。

WSGI将 web 组件分为三类: web服务器,web中间件,web应用程序, wsgi基本处理模式为 : WSGI Server -> (WSGI Middleware)* -> WSGI Application 。

CGI  FastCGI WSGI 解析

1、WSGI Server/gateway

wsgi server可以理解为一个符合wsgi规范的web server,接收request请求,封装一系列环境变量,按照wsgi规范调用注册的wsgi app,最后将response返回给客户端。文字很难解释清楚wsgi server到底是什么东西,以及做些什么事情,最直观的方式还是看wsgi server的实现代码。以python自带的wsgiref为例,wsgiref是按照wsgi规范实现的一个简单wsgi server。它的代码也不复杂。

CGI  FastCGI WSGI 解析

  1. 服务器创建socket,监听端口,等待客户端连接。
  2. 当有请求来时,服务器解析客户端信息放到环境变量environ中,并调用绑定的handler来处理请求。
  3. handler解析这个http请求,将请求信息例如method,path等放到environ中。
  4. wsgi handler再将一些服务器端信息也放到environ中,最后服务器信息,客户端信息,本次请求信息全部都保存到了环境变量environ中。
  5. wsgi handler 调用注册的wsgi app,并将environ和回调函数传给wsgi app
  6. wsgi app 将reponse header/status/body 回传给wsgi handler
  7. 最终handler还是通过socket将response信息塞回给客户端。

2、WSGI Application

wsgi application就是一个普通的callable对象,当有请求到来时,wsgi server会调用这个wsgi app。这个对象接收两个参数,通常为environ,start_response。environ就像前面介绍的,可以理解为环境变量,跟一次请求相关的所有信息都保存在了这个环境变量中,包括服务器信息,客户端信息,请求信息。start_response是一个callback函数,wsgi application通过调用start_response,将response headers/status
返回给wsgi server。此外这个wsgi app会return 一个iterator对象 ,这个iterator就是response body。这么空讲感觉很虚,对着下面这个简单的例子看就明白很多了。

3、WSGI MiddleWare

有些功能可能介于服务器程序和应用程序之间,例如,服务器拿到了客户端请求的URL, 不同的URL需要交由不同的函数处理,这个功能叫做 URL Routing,这个功能就可以放在二者中间实现,这个中间层就是 middleware。middleware对服务器程序和应用是透明的,也就是说,服务器程序以为它就是应用程序,而应用程序以为它就是服务器。这就告诉我们,middleware需要把自己伪装成一个服务器,接受应用程序,调用它,同时middleware还需要把自己伪装成一个应用程序,传给服务器程序。

其实无论是服务器程序,middleware 还是应用程序,都在服务端,为客户端提供服务,之所以把他们抽象成不同层,就是为了控制复杂度,使得每一次都不太复杂,各司其职。

有了以上三种接口之后,用户可以依据不同的接口来完成自己的应用程序的编写,写完后的程序如何与web服务器进行对接,针对不同的语言,不同的接口,有着不一样的工具。当然使用这些工具也可以直接启动服务,暴露给外部。

PHP-CGI

PHP-CGI是PHP自带的FastCGI管理器。PHP-CGI的不足:

  1. php-cgi变更php.ini配置后需重启php-cgi才能让新的php-ini生效,不可以平滑重启
  2. 直接杀死php-cgi进程php就不能运行了。(PHP-FPM和Spawn-FCGI就没有这个问题,守护进程会平滑从新生成新的子进程。)

Spawn-FCGI

Spawn-FCGI是一个通用的FastCGI管理服务器,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn-FCGI进行FastCGI模式下的管理工作,不过有不少缺点。而PHP-FPM的出现多少缓解了一些问题,但PHP-FPM有个缺点就是要重新编译,这对于一些已经运行的环境可能有不小的风险),在php 5.3.3中可以直接使用PHP-FPM了。Spawn-FCGI的代码很少,全部才630行,用c语言编写,最近一次提交是5年前。代码主页:https://github.com/lighttpd/spawn-fcgi

Spawn-FCGI代码分析如下:

  1. spawn-fcgi 首先create socket,bind,listen 3步创建服务器socket,(把这个socket叫做 fcgi_fd)
  2. 用dup2,把fcgi_fd 交换给 FCGI_LISTENSOCK_FILENO (FCGI_LISTENSOCK_FILENO数值上等于0,这是fastcgi协议当中指定用来listen的socket id)
  3. 执行execl ,replaces the current process image with a new process image. process image 进程在运行空间的代码段

很显然,Spawn-FCGI也是 pre-fork 模型,只是用了上古C语言编写,充满了N多 unix下暗黑编程技巧。

Spawn-FCGI功能很单一:

  1. 只管fork进程,子进程挂了,主进程仅仅log记录一次,根本不会重新fork。在2009年一段时间内,我曾经用spawn-fcgi部署php-cgi,当跑一段时间就会全挂掉,只能用crontab定时重启spawn-fcgi
  2. 不负责子进程中的网络IO,把socket放到指定位置就完了,接下来的事情由被spawn的程序处理

Spawn-FCGI是一个很早期的程序,瞻仰一下即可。另外有:1996年的一段代码:http://www.fastcgi.com/om_archive/kit/cgi-fcgi/cgi-fcgi.c,和spawn-fcgi一个风格

PHP-FPM

PHP-FPM是一个PHP FastCGI管理器,是只用于PHP的,可以在 http://php-fpm.org/download下载得到。PHP-FPM其实是PHP源代码的一个补丁,旨在将FastCGI进程管理整合进PHP包中。必须将它patch到你的PHP源代码中,在编译安装PHP后才可以使用。FPM(FastCGI 进程管理器)用于替换 PHP-CGI 的大部分附加功能,对于高负载网站是非常有用的。它的功能包括:

  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 的配置文件。

uWSGI

uWSGI 项目旨在为部署分布式集群的网络应用开发一套完整的解决方案。uWSGI主要面向web及其标准服务,已经成功的应用于多种不同的语言。由于uWSGI的可扩展架构,它能够被无限制的扩展用来支持更多的平台和语言。目前,你可以使用C,C++和Objective-C来编写插件。项目名称中的“WSGI”是为了向同名的Python Web标准表示感谢,因为WSGI为该项目开发了第一个插件。uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。uWSGI,既不用wsgi协议也不用FastCGI协议,而是自创了一个uwsgi的协议,uwsgi协议是一个uWSGI服务器自有的协议,它用于定义传输信息的类型(type
of information),每一个uwsgi packet前4byte为传输信息类型描述,它与WSGI相比是两样东西。据说该协议大约是fcgi协议的10倍那么快。

  1. uWSGI的主要特点如下:
  2. 超快的性能。
  3. 低内存占用(实测为apache2的mod_wsgi的一半左右)。
  4. 多app管理。
  5. 详尽的日志功能(可以用来分析app性能和瓶颈)。
  6. 高度可定制(内存大小限制,服务一定次数后重启等)。

其他拓展知识:Java Servlet、Sinatra、Rack

What is WSGI ?

WSGI is the Web Server Gateway Interface. It is a specification for web servers and application servers to communicate with web applications (though it can also be used for more than that)

WSGI是一种Web服务器网关接口。它是一个Web服务器(如nginx)与应用服务器(如uWSGI服务器)通信的一种规范。

关于WSGI协议看这里:WSGI

what is uWSGI ?

uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。

Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。

WSGI vs uwsgi(小写) vs uWSGI ?

uwsgi同WSGI一样是一种通信协议,而uWSGI是实现了uwsgi和WSGI两种协议的Web服务器。

uwsgi协议是一个uWSGI服务器自有的协议,它用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型描述,它与WSGI相比是两样东西。

关于uwsgi协议看这里:The uwsgi protocol

有了uWSGI为什么还需要nginx?

nginx具备优秀的静态内容处理能力,然后将动态内容转发给uWSGI服务器,这样可以达到很好的客户端响应。

注:通常python的web部署有两种

fcgi:用spawn-fcgi或者框架自带的工具对各个project分别生成监听进程,然后和http服务互动。

wsgi:利用http服务的mod_wsgi模块来跑各个project。

现在项目中用的部署是 nginx + uwsgi + django,说白了就是 是用uWSGI(前面的应该是大写) 利用wsgi来启动django。

上一篇:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)


下一篇:二十四种设计模式:访问者模式(Visitor Pattern)