这篇文章简要地介绍一下古老的 CGI(Common Gateway Interface,公共网关接口)。
静态文件服务
在互联网的早期一个时期,那时候的 WWW 网页服务还只是简单的静态页面服务。它的工作过程是这样的:
- 浏览器向服务器发起文件请求,比如:
GET /a.txt
- 服务器找到
a.txt
文件 - 服务器读取文件内容
- 服务器返回内容给浏览器
- 浏览器展示返回的内容
就像下面这张图描绘的那样:
动态内容服务
不过,人们很快就觉得,直接返回文件内容的方式太单调了,因为内容完全是固定不变的,不能动态地生成内容,看到不同的页面。 所以,如果想要每次的请求内容都不一样(比如:显示当前时间,显示登陆者的用户名),怎么办? 很简单,人们马上就想到了新的办法。
当服务器接收到浏览器的请求时,把请求的路径当成一个可执行文件,执行它,并把可执行文件的标准输出返回给浏览器。
它的工作过程是这样的:
- 浏览器向服务器发起一个请求,比如:
GET /cgi-bin/echo
- 服务器找到
/cgi-bin/echo
这个可执行文件 - 服务器把请求路径,HTTP头部等值作为环境变量传递给可执行文件
- 服务器把HTTP请求BODY作为标准输出传递给可执行文件
- 服务器执行可执行文件
- 可执行文件执行内部逻辑并把输出写入标准输出,然后退出
- 服务器读取可执行文件的标准输出
- 服务器把标准输出返回给浏览器
- 浏览器展示返回的内容
就像下面这张图描绘的那样:
由于可执行文件拥有自己的逻辑,所以它可以输出任意的内容。而不像静态文件那样,内容永远是固定不变的。这样一样,浏览器就可以看到动态的内容了。
我们通常把由静态文件生成的网页叫作静态网站,而把后端可执行文件动态生成的网页叫作动态网站。
后端的这个可执行文件通常叫作网关程序。而服务器与网关程序之间的这个通信协议,叫作公共网关接口(Common Gateway Interface, CGI)。
关于HTTP请求路径
先说点题外话。
几年前我用 C++ 写过一个 HTTP Web 服务器(这里),它也简单地支持CGI,(实现代码),包含但不限于常见的 lua、php。
它甚至支持把 Windows 的批处理脚本文件(.bat文件)、可执行文件(.exe文件)也当成网关程序来使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
实现这个功能,它至少改变了我对HTTP的一些“习惯性”的错误看法。那就是:
HTTP的服务器对请求路径拥有完全的解释权,跟请求的路径并没有绝对的关系。
什么意思?比如我发起一个GET /a.txt
的请求,很多人会认为它是打开一个TXT文件;发起一个GET /b.exe
,也会认为是下载一个程序文件。不会是执行,浏览器怎么可能执行EXE文件?
所以,其实并不是那样。有很多人这里都有误解,至少我当年是这样。
那么怎样决定呢?有很多方式,也不是都靠谱。多数时候,靠 HTTP 的 Content-Type
来猜测是靠谱的。
就是这样。
关于CGI的性能
对于现在的后端开发来说,纯使用CGI作为后端已经极其少见了。因为它有很多比较致命的不足:
-
对于每一个请求都要新启动一个进程来处理请求
新启动一个进程对操作系统来说开销特别大,必然造成性能不好。对于服务器的性能,一个简单的比较:同步 < 多进程 < 多线程 < 异步。
-
进程处理完请求就立即退出
立即退出意味着请求与请求之间很难产生联系,连简单的数据共享都做不到。所以后来出现了FastCGI,进程不用退出了。
所以,后来的服务器就不怎么再支持CGI了。比如 nginx。