CGI 即 Common Gateway Interface,译作“通用网关接口”。初次听闻,略感疑惑,实则每个字眼都值得玩味。
通用,是一个显著特性。虽然我们听说过Java的Servlet,Python的WSGI。但其实Java、Python都是支持CGI的,不仅如此,其他我们所熟知的语言大多也支持。理论上来说,所有支持标准输出,支持获取环境变量的编程语言都能用来编写CGI程序。
早期的web服务器,只能响应浏览器发来的HTTP镜头资源请求,并将存储在服务器中的静态资源返回给浏览器。随着Web技术的发展,逐渐出现了动态技术,但是Web服务器并不能直接运行动态脚本。为了解决Web服务器与外部应用程序(CGI程序)之间的数据互通,于是出现了CGI(Common Gateway Interface)通用网关接口。简单理解,可以认为GUI就是Web服务器和运行其上的应用程序进行“交流”的一种约定
PS: 其实,脚本(script)并不一定就是脚本语言编写的。脚本描述的是一类程序的特征:为了完成某一任务,用程序实现批量执行一组常用逻辑的组合。凡是符合这一特征的程序都可称作脚本。当然,时至今日,脚本语言所编写的程序都可以称之为脚本程序,并且其内部逻辑也早已变得并不简单。
Gateway,网关。通常意识中,网关一词更多的是硬件层面的概念,但其实与CGI的网关二字之含义也是不谋而合的。称CGI为软件网关也不为过
请注意区分Web Server和后台Server。
Interface:Come On。又是一个被国人翻译烂掉的词汇。API(应用程序接口)的I是它,被译作“接口”。UI(用户界面)的I也是它,被译作“界面”。实际他们是同样的意思。我更喜欢“接口”一词。
接口,确切而言是“接口协议”,熟悉网络的同学们,肯定都明白“协议”是什么。所谓协议,既是通信双方或多方都共识并遵守的一套规则。就像红灯停,绿灯行,上下车道靠右行。只有全国人民都遵守这个规则,交通才不会出什么乱子。实际上规定绿灯停,红灯行,上下车道靠左行可不可以呢?当然可以,英国日本都是靠左行的,关键是全国人民的认知要一致,这个共识很重要。
举几个栗子:TCP/IP这类二进制协议,协议内容的描述是某某字节是干嘛滴,其取值范围是什么,不同取值又是什么含义。HTTP协议是字符协议,这类协议内容的描述就是第一行是啥,第二行是啥……,应该出现啥单词,表示啥意思。
其实不管是TCP/IP或是HTTP,都可以统称为“网络协议”,粗浅一点理解就是“描述报文内容详细语义的协议”。而“接口协议”却不然,它不会定义哪些字节该写什么,也不会定义字符的内容规范。CGI其实是构架在HTTP协议之上的。它描述的是另一个维度的共识标准
CGI是Web服务器和一个独立的进程之间的协议
通过CGI接口,Web服务器就能够获取客户端传递的数据,并转交给服务器端的CGI程序处理,然后返回结果给客户端。
简单来说,CGI实际上是一个接口标准,而通常所说的CGI指代其实是CGI程序,也就是说实现了CGI接口标准的程序,只要编程语言具有标准输入、标准输出和环境变量,就可以用来编写CGI程序
对于一个CGI程序,主要的工作是从环境变量和标准输入中读取数据,然后处理数据,最后向标准输出中输出数据。
Web服务器在接收到用户浏览器的HTTP请求,比如请求如下URL:
http://guodongxiaren.me/cgi-bin/helloworld.cgi
而CGI如何构造出数据(比如HTML页面)返回给浏览器的呢?
cout<<"Content-Type:text/html\n\n"<<endl;
注意最后要有两个换行符&#xff0c;这是必须有的。因为HTTP协议本身就是一个字符协议。它需要通过一个空行来区分哪里是报头&#xff0c;哪里是实体。在两个换行符&#xff08;即一个空行&#xff09;之后就可以愉快的输出HTML&#xff08;即实体部分&#xff09;了。
GET请求&#xff0c;它将数据打包放置在环境变量QUERY_STRING中&#xff0c;CGI从环境变量QUERY_STRING中获取数据。
常⻅的环境变量如下表所示&#xff1a;
环境变量的大小是由一定的限制的&#xff0c;当需要传送的数据量大时&#xff0c;存储环境变量的空间可能会不足&#xff0c;造成数据接收不完全&#xff0c;甚至无法执行CGI程序。
因此后来有发展处另一种方法&#xff1a;POST&#xff0c;也就是利用IO重新导向的技巧&#xff0c;让CGI程序可以由stdin和stdout直接跟浏览器沟通
当我们使用这种方法传递请求的数据时&#xff0c;web服务器收到数据后会先放在一块输入缓冲区中&#xff0c;并且将数据的大小记录在CONTENT_LENGTH这个环境变量&#xff0c;然后调用CGI程序的stdin指向这块缓冲区&#xff0c;于是我们就可以通过stdin和环境变量CONTENT_LENGTH得到所有的信息&#xff0c;就没有信息大小的限制了
CGI编写Web程序虽然看似解析组装等操作十分繁琐&#xff0c;但其实都有很多第三方的封装来简化这些操作&#xff0c;高级语言的标准库基本都已经做了封装&#xff0c;而针对C&#43;&#43;则有一个还不错的第三方库Cgicc。
CGI程序有一不大不小的缺陷&#xff0c;缺乏URL路由的功能&#xff0c;基本上一个CGI都是独立提供给外界访问&#xff0c;一个CGI就是独立的可执行程序。因此不仅CGI的URL比较丑陋&#xff0c;而且容易暴露真实路径。
CGI一般只做上层目录的路由&#xff0c;而这只能交给Web服务器去配置。比如&#xff1a;http://app/hello.cgi 的app目录在物理OS的什么位置可通过Apache去配置。虽然理论上讲CGI程序也可以实现http://app/hello.cgi/abc/def 这种形式的路由。但是基本上没人这样做。
web服务器与CGI通过环境变量、标准错误、标准输出、标准错误相互传递数据。在遇到用户连接请求&#xff1a;
CGI使得外部程序与web服务器之间交互称为可能。CGI程序运行在独立的进程中&#xff0c;并对每个Web请求建⽴⼀个进程&#xff0c;这种⽅法⾮常容易实现&#xff0c;但效率很差&#xff0c;难以扩展。面对大量请求&#xff0c;进程的大量建立和消亡使操作系统性能大大下降。此外&#xff0c;由于地址空间无法共享&#xff0c;也限制了资源重用
既然有这么多现成的库做了封装&#xff0c;那么理应用CGI编写Web的也不少才对。其实不然&#xff0c;这是因为CGI有一大硬伤&#xff1a;
当然FCGI其实也并不是什么惊世骇俗的创意&#xff0c;很容易联想到的解决思路。资源池是后台性能优化中的常见套路。Java发明的Servlet技术也是一种常驻内存的网关通信技术&#xff0c;只不过它采用的是多线程而非进程。
快速通⽤⽹关接⼝(Fast Common Gateway Interface&#xff0f;FastCGI)是通⽤⽹关接⼝(CGI)的改进&#xff0c;描述了客户端和服务器程序之间传输数据的⼀种标准。
FastCGI致力于减少Web服务器与CGI程序之间互动的开销&#xff0c;从而使服务器可以同时处理更多的web请求。与为每个请求创建一个新的进程不同&#xff0c;FastCGI使用持续的进程来处理一连串的请求&#xff0c;这些进程有FastCGI进程管理器管理&#xff0c;而不是web服务器。
nginx服务支持FastCGI模式&#xff0c;能够高效的处理动态请求。⽽nginx对应的FastCGI模块为&#xff1a;ngx_http_fastcgi_module。ngx_http_fastcgi_module模块允许将请求传递给FastCGI服务器
由于FastCGI程序并不需要不断的产⽣新进程&#xff0c;可以⼤⼤降低服务器的压⼒并且产⽣较⾼的应⽤效率。它的速度效率最少要⽐CGI 技术提⾼ 5 倍以上。它还⽀持分布式的部署&#xff0c;即FastCGI 程序可以在web 服务器以外的主机上执⾏。
CGI就是所谓的短生存期应用程序&#xff0c;FastCGI是所谓的长生存期应用程序。FastCGI像是一个常驻型的CGI&#xff0c;它可以一直执行着&#xff0c;不会每次都要花费时间去fork一次。
我们知道&#xff0c;CGI可以直接吐出一个html网页&#xff0c;也可以进行各种计算、逻辑处理任务。但随着各类web前后端技术的发展&#xff0c;以及大数据、高并发的Server使用场景越来越多。现代的CGI的用法&#xff0c;在发生变化。越来越多的任务从后端转移到前端&#xff0c;前端页面利用强大的JS承担起更多的责任。