以前刚开始学 Web开发 的时候都是用 Java 进行编写的,Java 正常都是采用的 Tomcat 或者 Jetty 做容器,然后通过
Servlet/JSP 编写动态的处理逻辑,然后提供服务。那个时候还没有想过要是用 C/C++ 等其他语言写动态网页怎么办?那时也偶尔听到
CGI 的名词,但是一直没明白什么意思,我觉得一个很大的原因是因为我没有使用 Apache/Nginx 的问题,因为 Tomcat 自带
Web服务器,直接运行即可访问。

话说回来,如果要用 C/C++ 编写 动态Web 的话,以前流行的方式是 CGI,现在的话好像似乎 FastCGI
或者说已经转其他语言编写了。在本文,我将向大家介绍一下什么是 CGI/FastCGI 以及它们的处理方式。最后,我还将会与 WSGI
进行一个简单的对比。

CGI

CGI 的全称是 Common Gateway Interface(通用网关接口),其实就是说 Web服务器 如何获得
CGI程序 的计算结果,然后返回给客户端,这里的 CGI程序 其实就是一个可执行的程序,可以是
Python脚本/PERL脚本/SHELL脚本/C/C++程序等。

同时,CGI程序 的目录放置还是有要求的,对于 Apache 来说,需要放置到 cgi-bin 目录下,不然是不生效的。下面就是一个简单的
CGI程序,它是用 Python 编写的。

  1. #!/usr/bin/python
  2. # -*- coding: UTF-8 -*-
  3. print "Content-type:text/html"
  4. print # 空行,告诉服务器结束头部
  5. print '<html>'
  6. print '<head>'
  7. print '<meta charset="utf-8">'
  8. print '<title>Hello Word - 我的第一个 CGI 程序!</title>'
  9. print '</head>'
  10. print '<body>'
  11. print '<h2>Hello Word! 我是你的第一CGI程序</h2>'
  12. print '</body>'
  13. print '</html>'

使用 CGI 技术,Web服务器 在接收到请求之后,如果发现是需要调用 CGI程序 的,那么它会 fork 一个新进程来运行外部
CGI程序,这个进程在处理完之后,把处理后的结果返回给 Web服务器,最后 Web服务器 将内容转交给客户端,然后这个 fork
的进程也随之退出。每个请求都是重复这个过程。

例如,我们上面的 python CGI程序,如果有客户端请求的是这个 CGI程序,那么 Web服务器 就会 fork 一个进程执行它,然后将执行结果返回给
Web服务器,然后 Web服务器 再将输出返回给客户端。

这里,我们可以发现一个很明显的问题,那么就是每个请求都需要 fork 一个进程,然后完了之后又退出,每次都这样,烦不烦呢?所以,基于这个问题,FastCGI
出现了。

FastCGI

FastCGI 的功能和 CGI 的基本类似,但是,不同之处在于 Web服务器 收到客户端的一个请求时,他不会重新 fork 出一个进程(在 Web服务器
第一次启动时就已经 fork 出了一个进程,而且不退出),Web服务器 直接把请求传递给这个进程(FastCGI 使用了 TCP
通信),这个进程收到请求后进行处理,把结果返回给 Web服务器,然后,这个进程是不退出的,而是等待下一个请求。

在 nginx 中,常见的 fastcgi 配置如下:

  1. location / {
  2. include fastcgi_params;
  3. fastcgi_param PATH_INFO $fastcgi_script_name;
  4. fastcgi_param SCRIPT_NAME "";
  5. fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
  6. }

这里还需要附带一个 fastcgi_params,用于传递参数,这是一个文件,目录一般也在 /etc/nginx 上,默认内容为:

  1. fastcgi_param QUERY_STRING $query_string;
  2. fastcgi_param REQUEST_METHOD $request_method;
  3. fastcgi_param CONTENT_TYPE $content_type;
  4. fastcgi_param CONTENT_LENGTH $content_length;
  5. fastcgi_param SCRIPT_NAME $fastcgi_script_name;
  6. fastcgi_param REQUEST_URI $request_uri;
  7. fastcgi_param DOCUMENT_URI $document_uri;
  8. fastcgi_param DOCUMENT_ROOT $document_root;
  9. fastcgi_param SERVER_PROTOCOL $server_protocol;
  10. fastcgi_param REQUEST_SCHEME $scheme;
  11. fastcgi_param HTTPS $https if_not_empty;
  12. fastcgi_param GATEWAY_INTERFACE CGI/1.1;
  13. fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
  14. fastcgi_param REMOTE_ADDR $remote_addr;
  15. fastcgi_param REMOTE_PORT $remote_port;
  16. fastcgi_param SERVER_ADDR $server_addr;
  17. fastcgi_param SERVER_PORT $server_port;
  18. fastcgi_param SERVER_NAME $server_name;
  19. # PHP only, required if PHP was built with --enable-force-cgi-redirect
  20. fastcgi_param REDIRECT_STATUS 200;

WSGI

关于 WSGI 的介绍在我的博客中已经多次提到了,所以对于还不清楚的同学可以阅读一下这篇wsgi
简单介绍 了解一下。

WSGI 其实和 FastCGI 差不多,只不过是 WSGI 的层次比 FastCGI 更高,是一种协议。

对比

这里对本文介绍的三种协议进行一个对比,对比一下各自的优缺点:

类型 优点 缺点
CGI 早期的动态脚本,可以使用多种程序语言 反复加载,性能低下,都要经过『启动进程、处理请求、结束进程』 阶段。保持状态有难度。
FastCGI Web服务器和应用服务器分离,支持反向代理 配置稍复杂,nginx 支持不友好
WSGI Web服务器和应用服务分离,支持负载均衡和请求转发,避免阻塞 支持语言有限,仅限 Python

扩展名词

参考文献