一、CGI是什么:
关于CGI是什么,这篇文章已经讲的非常清楚:
下面简单概括一下关于CGI的基本概念:
CGI = Common Gateway Interface,“通用网关接口”:
(1)“通用”,几乎所有的语言都支持CGI,都可以拿来编写CGI程序;
(2)“网关”,更形象的叫法是“协议翻译机”。通常与网关输入输出两端通信使用的是不同的协议,比如一方是HTTP,另一方可能是其他协议,比如企业内部自定义的协议等;
CGI程序通常部署在Web服务器上(Nginx、Apache),Web服务器调用CGI程序;
(3)“接口”,接口,API。
什么是接口协议:
协议是什么自不必说,通信双方或多方共同遵守的约定。
但是协议也分很多种:
二级制协议 - - - TCP/IP,协议内容是某某某字节、某某某比特位是干嘛的;
字符协议 - - - HTTP,描述第一行是干嘛的,第二行是干嘛的,没固定长度,没固定格式,按字符关键字去匹配;
这两种都是“网络协议”,描述报文内容详细语义的协议。
但是接口协议不同,接口协议一般指的是进行交换的接口间需要遵从的通信方式和要求,例如USB、Type-C、HDMI等。
CGI是如何工作的:
CGI程序本质上是OS操作系统上的一个普通的可执行的应用程序,
CGI可以理解为Web服务进程(Nginx进程、Apache进程)与一个运行在服务器上的CGI进程之间的协议,CGI会把HTTP请求消息中的请求头(Header)设置成进程的环境变量,把HTTP请求的包体设置成标准输入STDIN,进程的标准输出设置成HTTP响应(包含响应头和体),即返回一个HTML。
所以,CGI就是 Web服务器进程(Nginx、Apache)与 CGI进程 交互 的接口,输入HTTP请求(头和体),返回HTTP响应(头和体)。
CGI与Web应用服务器(Server)有何区别:
Nginx收到客户端的HTTP请求后有几种处理方式,对于静态资源的请求可直接返回资源给客户端,那么何时需要转发请求给应用服务器(如Tomcat),何时需要转发请求给CGI?
CGI与应用服务器的最大区别是:
Nginx与应用服务器之间通信使用的是HTTP协议,与CGI通信使用的标准输入输出流。
也就是说,CGI计算后产生的结果,是可以直接使用printf()在客户端上打印出来的(使用的标准输出流STDOUT),这样就有一些web场景必须要使用CGI,例如leetcode、实验楼等类型的在线编译工具,用户输入一段代码后编译结果就能在页面上打印显示出来。
(至于CGI如何计算,这个需要开发者去编写CGI的业务代码。)
二、CGI的缺点与FastCGI的引出:
CGI有一大硬伤:
每次HTTP请求CGI,Web服务器都要启动一个新的进程去运行这个CGI程序(颇具Unix特色的 fork-and-excute)。
当用户量巨大时,这样的操作会严重拖慢Web服务器的性能。
因此引出FastCGI,即一个常驻的进程池,由调度器负责将传递来的CGI请求传递给进程处理。
所以,FastCGI即使常驻型的CGI。
三、在Nginx中如何使用FastCGI:
1. 安装FastCGI库:
下载安装包后解压安装:
./configure
make
make install
2. 安装spawn-fcgi:
./configure
make
cp /src/spawn_fcgi /usr/local/nginx/sbin # 拷贝spawn_fcgi可执行程序到/nginx/sbin目录下
3. 编写一个FastCGI的测试小程序:
#include <stdio.h>
#include <fcgi_stdio.h>
int main() {
while(FCGI_Accept() >= 0) {
printf("Content-type: text/html\r\n");
printf("\r\n");
printf("<title>fast CGI Hello!</title>");
printf("<h1>This is My CGI!</h1>");
printf("Thank you, CGI.\n");
}
}
编译生成可执行文件:
gcc cgi_test.c -o cgi_test -lfcgi //动态编译
gcc cgi_test.c -o cgi_test /usr/local/lib/libfcgi.a //静态编译
(本机电脑中有点问题,动态编译一直报错找不到libfcgi.so,解决方法是在fcgi.conf的配置文件中指定动态链接库的位置:动态编译解决方法)
4. 使用 spawn-fcgi 工具启动 cgi_test 程序:
cgi_test从本质上说只是一个普通的Linux进程,单独的运行它(./cgi_test)不会起任何作用,因此需要借助spawn-fcgi 去指定 cgi_test 的监听IP地址和端口号。
cd /usr/local/sbin
./spawn-fcgi -a 127.0.0.1 -p 9002 -f /usr/local/cgi_test
spawn-fcgi指定cgi_test绑定IP地址127.0.0.1、监听端口号9002,启动程序的绝对路径所在的cgi_test。
启动成功后会打印提示:
spawn-fcgi: child spawned successfully: PID: 5432
注: spawn-fcgi命令的参数:
./spawn-fcgi -h # 查看所有参数,其中主要的有:
-f <path> # filename of the fcgi-application
-a <address> # bind to IPv4/IPv6 address
-p <port> # bind to TCP port
5. 编写Nginx的配置文件:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 9000;
location ~\ .cgi {
fastcgi_pass 127.0.0.1:9002;
fastcgi_index index.cgi;
fastcgi_param SCRIPT_FILENAME cgi$fastcgi_script_name;
include ../conf/fastcgi_params;
}
}
}
关于配置文件中配置项的含义:
fastcgi_pass :
# 类似于proxy_pass,也是服务器代理的功能。
# proxy_pass指示将HTTP请求转到upstream中,fastcgi_pass则指示将请求转给后面标注的IP地址端口
fastcgi_index index.cgi :
# 设置了fastcgi默认使用的脚本。就是当SCRIPT_FILENAME没有命中脚本的时候,使用的就是fastcgi_index设置的脚本。
fastcgi_params:
# 设置fastcgi请求中的参数,具体设置的东西可以在$_SERVER中获取到
随后使用编写的配置文件,启动Nginx:
./sbin/nginx -c my_conf/my_conf.conf
如何实现FastCGI负载均衡:
http {
upstream fastcgi_end {
server 127.0.0.1:9002 weight = 2;
server 127.0.0.1:9005 weight = 1;
}
server {
listen 9000;
location ~\ .cgi {
fastcgi_pass fastcgi_end;
}
}
}
6. 最后,在浏览器中访问cgi资源进行验证:
http://127.0.0.1:9000/123.cgi
显示页面:
因为Nginx配置文件中的策略是凡是 “.cgi” 后缀结尾的请求一律转到CGI程序处理,所有浏览器中输入的URI可以是任意名字的CGI文件,例如 “123.cgi”。
Tips:
1. Nginx配置文件中的正则表达式:
http {
server {
location ~\ .cgi {
}
}
server {
location ~\ .(mp3|mp4) {
}
}
}
~\ .cgi
表示客户端的HTTP请求中的URI凡是以 “.cgi” 结尾的文件,都转到这个本location指示的服务器资源上去;~\ .(mp3|mp4)
表示以 “.mp3” 或者 “.mp4” 后缀结尾的请求。
2. 网关和路由器的区别:
简而言之,连接两个网络的设备,都是网关。
网关是一个大的概念,不具体特指一类产品,只要连接两个不同网络的设备都可以叫网关,路由器可以实现网关的功能。所谓网关,可以是路由器、三层交换机、防火墙等。
遗留问题:
FastCGI内部的工作原理是什么?
CGI都提供哪些更高级的功能?如何编写一个更复杂的CGI程序?