大家好,又见面了,我是你们的朋友全栈君。
一、什么是CGI 通用网关接口(Common Gateway Interface、CGI)描述了客户端和 服务器 程序之间传输数据的一种标准 ,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。 CGI 独立于任何语言的,CGI 程序可以用任何脚本语言或者是完全独立编程语言实现,只 要这个语言可以在这个系统上运行。Unix shell script、Python 、Ruby、PHP、perl、Tcl、C/C++ 和 Visual Basic 都可以用来编写 CGI 程序 最初,CGI 是在 1993 年由美国国家超级电脑应用中心(NCSA)为 NCSA HTTPd Web 服务 器开发的。这个 Web 服务器使用了 UNIX shell 环境变量 来保存从 Web 服务器传递出去的参数,然后生成一个运行 CGI 的独立的进程 CGI处理流程
1. web 服务器收到客户端(浏览器)的请求 Http Request,启动 CGI 程序,并通过环境变量、 标准输入传递数据 2. CGI 进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如 数据库服务 器)、 逻辑处理等 3. CGI 进程将处理结果通过标准输出、标准错误,传递给 web 服务器 4. web 服务器收到 CGI 返回的结果 ,构建 Http Response 返回给客户端,并杀死 CGI 进程 web 服务器与 CGI 通过环境变量、标准输入、标准输出、标准错误互相传递数据 。在遇到用户连接请求: 先要创建 CGI 子进程,然后 CGI 子进程处理请求,处理完事退出这个子进程: fork-and-execute CGI 方式是客户端有多少个请求,就开辟多少个子进程 ,每个子进程都需要启动自己的 解释器 、加载配置,连接其他服务器等初始化工作,这是 CGI 进程性能低下的主要原 因。当用户请求非常多的时候,会占用大量的内存、cpu 等资源,造成性能低下 CGI 使外部程序与 Web 服务器之间交互成为可能。CGI 程序运行在独立的进程中,并对每 个 Web 请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请 求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限 制了资源重用 常用环境变量
环境变数 含义 AUTH_TYPE 存取认证类型 CONTENT_LENGTH 由标准输入传递给 CGI 程序的数据长度,以 bytes 或字元数来 计算 CONTENT_TYPE 请求的 MIME 类型 GATEWAY_INTERFACE 服务器的 CGI 版本编号 HTTP_ACCEPT 浏览器能直接接收的 Content-types, 可以有 HTTP Accept header 定义 HTTP_USER_AGENT 递交表单的浏览器的名称、版本和其他平台性的附加信息 HTTP_REFERER 递交表单的文本的 URL,不是所有的浏览器都发出这个信息, 不要依赖它 PATH_INFO 传递给 CGI 程序的路径信息 QUERY_STRING 传递给 CGI 程序的请求参数,也就是用”?”隔开,添加在 URL 后面的字串 REMOTE_ADDR client 端的 host 名称 REMOTE_HOST client 端的 IP 位址 REMOTE_USER client 端送出来的使用者名称 REMOTE_METHOD client 端发出请求的方法(如 get、post) SCRIPT_NAME CGI 程序所在的虚拟路径,如/cgi-bin/echo SERVER_NAME server 的 host 名称或 IP 地址 SERVER_PORT 收到 request 的 server 端口 SERVER_PROTOCOL 所使用的通讯协定和版本编号 SERVER_SOFTWARE server 程序的名称和版本
标准输入
环境变量的大小是有一定的限制的,当需要传送的数据量大时,储存环境变量的空间可能会不足 ,造成数据接收不完全,甚至无法执行 CGI 程序 因此后来又发展出另外一种方法:POST,也就是利用 I/O 重新导向的技巧,让 CGI 程序可以由 stdin 和 stdout 直接跟浏览器沟通 当我们指定用这种方法传递请求的数据时,web 服务器收到数据后会先放在一块输入缓冲区中,并且将数据的大小记录在 CONTENT_LENGTH 这个环境变量,然后调用 CGI 程序并将 CGI 程序的 stdin 指向这块缓冲区,于是我们就可以很顺利的通过 stdin 和环境变数 CONTENT_LENGTH 得到所有的信息,再没有信息大小的限制了 使用场景: 例如牛客网等平台中的在线编程工具,网页客户端将数据当做输入传递给后端的cgi程序,cgi程序处理完成之后将结果作为输出返回给客户端cgi与server进程的区别: cgi使用输入输出与客户端进行交互,但是server使用的是http等协议与客户端进行交互二、FastCGI 快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。FastCGI 致力于减少 Web 服务器 与 CGI 程式之间互动的开销 ,从而使服务器可以同时处理更多的 Web 请求。与为每个请求 创建一个新的进程不同,FastCGI 使用持续的进程来处理一连串的请求。这些进程由 FastCGI 进程管理器管理(例如下面我们要介绍的spawn-fcgi),而不是 web 服务器 由于 FastCGI 程序并不需要不断的产生新进程 ,可以大大降低服务器的压力并且产生较高的 应用效率。它的速度效率最少要比 CGI 技术提高 5 倍以上。它还支持分布式的部署,即 FastCGI 程序可以在 web 服务器以外的主机上执行CGI 是所谓的短生存期应用程序,FastCGI 是所谓的长生存期应用程序 。FastCGI 像是一个常驻(long-live)型的 CGI,它可以一直执行着,不会每次都要花费时间去 fork 一次(这 是 CGI 最为人诟病的 fork-and-execute 模式)总结起来就是: cgi是一个请求(一个客户端)对应一个进程,一个请求来时就创建一个cgi进程,请求结束后销毁该cgi进程 FasiCGI就是在后端申请一个进程池,请求来了之后从进程池中取进程,而不用每次创建销毁 FastCGI处理流程
1.Web 服务器启动时载入初始化 FastCGI 执行环境 。 例如 IIS、ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi 2.FastCGI 进程管理器自身初始化 ,启动多个 CGI 解释器进程并等待来自 Web 服务器 的连接。启动 FastCGI 进程时,可以配置以 ip 和 UNIX 域 socket 两种方式启动 3.当客户端请求到达Web 服务器时,Web 服务器将请求采用socket方式转发FastCGI 主进程 ,FastCGI 主进程选择并连接到一个 CGI 解释器。Web 服务器将 CGI 环境变量和标准输入发送到 FastCGI 子进程 4.FastCGI 子进程完成处理后将标准输出和错误信息从同一 socket 连接返回 Web 服务 器。当 FastCGI 子进程关闭连接时,请求便处理完成 5.FastCGI 子进程接着等待 并处理来自 Web 服务器的下一个连接 常用环境变量
SCRIPT_FILENAME document_root fastcgi_script_name;#脚本文件请求的路径 QUERY_STRING $query_string; #请求的参数;如?app=123 REQUEST_METHOD $request_method; #请求的动作(GET,POST) CONTENT_TYPE $content_type; #请求头中的 Content-Type 字段 CONTENT_LENGTH $content_length; #请求头中的 Content-length 字段。 SCRIPT_NAME $fastcgi_script_name; #脚本名称 REQUEST_URI $request_uri; #请求的地址不带参数 DOCUMENT_URI document_uri; #与 uri 相同。 DOCUMENT_ROOT $document_root; #网站的根目录。在 server 配置中 root 指令中指定的值 SERVER_PROTOCOL $server_protocol; #请求使用的协议,通常是 HTTP/1.0 或 HTTP/1.1。 GATEWAY_INTERFACE CGI/1.1;#cgi 版本 SERVER_SOFTWARE nginx/$nginx_version;#nginx 版本号,可修改、隐藏 REMOTE_ADDR $remote_addr; #客户端 IP REMOTE_PORT $remote_port; #客户端端口 SERVER_ADDR $server_addr; #服务器 IP 地址 SERVER_PORT $server_port; #服务器端口 SERVER_NAME $server_name; #服务器名,域名在 server 配置中指定的 server_name PATH_INFO $path_info;#可自定义变量
三、ngx_http_fastcgi_module模块 Nginx有一个fast_cgi模块 (ngx_http_fastcgi_module模块),其能与任何兼容FastCGI协议的服务器通信,该模块通过 fastcgi 协议将指定的客户端请求转发至 spawn-fcgi 处理 工作原理
Web浏览器通过HTTP请求将数据发送给Nginx,但是Nginx的数据都是HTTP格式的,那么FastCGI如何处理这些数据呢? 此时ngx_http_fastcgi_module模块会将Nginx的HTTP协议转换为fastcgi协议 ,然后将数据转发给FastCGI程序进行处理 模块的Nginx配置文件参数
四、FastCGI通信协议 FastCGI 是二进制连续传递的, 定义了一个统一结构的消息头,用来读取每个消息的消息体, 方便消息包的切割。一般情况下, 最先发送的是 FCGI_BEGIN_REQUEST 类型的消息,然后是 FCGI_PARAMS 和 FCGI_STDIN 类型 的消息,当 FastCGI 响应处理完后,将发送 FCGI_STDOUT 和 FCGI_STDERR 类型的消息,最后以 FCGI_END_REQUEST 表示请求的结束 FCGI_BEGIN_REQUEST 和 FCGI_END_REQUEST 分别表示请求的开始和结束,与整个协议相关 FastCGI 协议类型
#define FCGI_BEGIN_REQUEST 1 //(web->fastcgi)请求开始数据包 #define FCGI_ABORT_REQUEST 2 //(web->fastcgi)终止请求 #define FCGI_END_REQUEST 3 //(fastcgi->web)请求结束 #define FCGI_PARAMS 4 //(web->fastcgi)传递参数 #define FCGI_STDIN 5 //(web->fastcgi)数据流 传输数据 #define FCGI_STDOUT 6 //(fastcgi->web)数据流传输数据 #define FCGI_STDERR 7 //(fastcgi->web)数据流传输 #define FCGI_DATA 8 //(web->fastcgi)数据流传输 #define FCGI_GET_VALUES 9 //(web->fastcgi)查询 fastcgi 服务器性能参数 #define FCGI_GET_VALUES_RESULT 10 //(fastcgi->web)fastcgi 性能参数查询返回 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
由 web 服务器向 FastCGI 程序传输的消息类型有以下几种: FCGI_BEGIN_REQUEST 表示一个请求的开始 FCGI_ABORT_REQUEST 表示服务器希望终止一个请求 FCGI_PARAMS 对应于 CGI 程序的环境变量,php $_SERVER 数组中的数据绝大 多数来自于此 FCGI_STDIN 对应 CGI 程序的标准输入,FastCGI 程序从此消息获取 http 请求 的 POST 数据 FCGI_DATA FCGI_GET_VALUES 由 FastCGI 程序返回给 web 服务器的消息类型有以下几种: FCGI_STDOUT 对应 CGI 程序的标准输出,web 服务器会把此消息当作 html 返 回给浏览器 FCGI_STDERR 对应 CGI 程序的标准错误输出, web 服务器会把此消息记录到 错误日志中 FCGI_END_REQUEST 表示该请求处理完毕 FCGI_UNKNOWN_TYPE FastCGI 程序无法解析该消息类型 FCGI_GET_VALUES_RESULT FastCGI请求传递过程
Web 服务器发送 FastCGI 请求时: 依次发送了 3 类 Record,类型分别为 BEGIN_REQUEST、PARAMS 和 STDINFastCGI 进程返回 FastCGI 响应时: 依次返回了 3 类 Record,类型分别为 STDOUT、STDERR、END_REQUEST FastCGI 数据包格式
FastCGI 数据包两部分: 头部(header),包体(body)每个数据包都必须包含 header,body 可以没有。header 为 8 个字节, body 必须为 8 的整数倍, 不是的话需要填充 数据包头部: typedef struct { unsigned char version; // 版本号 unsigned char type; // 数据包类型 unsigned char requestIdB1; // 记录 id 高 8 位 unsigned char requestIdB0; // 记录 id 低 8 位 unsigned char contentLengthB1; // 记录内容长度高 8 位(body 长度高 8 位) unsigned char contentLengthB0; // 记录内容长度低 8 位(body 长度低 8 位) unsigned char paddingLength; // 补齐位长度(body 补齐长度) unsigned char reserved; // 补齐位 }FCGI_Header;
数据包包体: FCGI_BEGIN_REQUEST 类型记录的 contentData 数据部分的结构 FCGI_END_REQUEST 类型记录的 contentData 数据部分的结构 typedef struct { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5]; } FCGI_BeginRequestBody; typedef struct { FCGI_Header header; FCGI_BeginRequestBody body; } FCGI_BeginRequestRecord; typedef struct { unsigned char appStatusB3; unsigned char appStatusB2; unsigned char appStatusB1; unsigned char appStatusB0; unsigned char protocolStatus; unsigned char reserved[3]; } FCGI_EndRequestBody; typedef struct { FCGI_Header header; FCGI_EndRequestBody body; } FCGI_EndRequestRecord; typedef struct { unsigned char type; unsigned char reserved[7]; } FCGI_UnknownTypeBody; typedef struct { FCGI_Header header; FCGI_UnknownTypeBody body; } FCGI_UnknownTypeRecord;
五、运行FastCGI程序的环境搭建 需要准备两样东西: cgi开发库:其提供相关cgi程序的接口,可以用来开发cgi程序 spawn-fcgi:(FastCGI的进程管理器)cgi程序编写好之后,需要用spawn-fcgi来运行cgi程序 五、cgi开发库 使用 C/C++编写 FastCGI 应用程序,可以使用 FastCGI 软件开发 套件或者其它开发框架,如 fcg cgi开发库的编译与安装
wget ftp://ftp.slackware.com/.2/gentoo/distfiles/fcgi-2.4.0.tar.gz tar zxf fcgi-2.4.0.tar.gz
cd fcgi-2.4.0 ./configure
make
sudo make install
默认安装完成之后,就可以在下面的目录中查看到对应的文件了 fcgi的头文件被存放到:/usr/local/include fcgi的动态库文件被存放到:/usr/local/lib/ sudo /sbin/ldconfig
六、spawn-fcgi(FasiCGI进程管理器) Nginx不能像Apache那样直接执行外部可执行程序,但Nginx可以作为代理服务器,将请求转发给后端服务器,这也是Nginx的主要作用之一。其中 Nginx 就支持FastCGI代理,接收客户端的请求,然后将请求转发给后端FastCGI进程 由于FastCGI进程由FastCGI进程管理器管理,而不是Nginx。这样就需要一个 FastCGI 进程管理器,管理我们编写 FastCGI 程序 spawn-fcgi是一个通用的 FastCGI 进程管理器, 简单小巧,原先是属于 lighttpd 的一部分, 后来由于使用比较广泛,所以就迁移出来作为独立项目(此处我们使用C语言编写的FastCGI程序,因此用spawn-fcgi进行管理器,其他的进程管理器还有php-fpm(管理PHP的)等)spawn-fcgi 使用 pre-fork 模型, 功能主要是打开监听端口,绑定地址,然后 fork-and-exec 创建我们编写的 FastCGI 应用程序进程,退出完成工作。FastCGI 应用程序初始化,然后进入死循环侦听 socket 的连接请求 FastCGI 协议、spawn-fcgi、Nginx 三者关系
Nginx是web服务器, 只提供HTTP协议的输入和输出spawn-fcgi 服务器, 只支持Fastcgi协议的输入和输出它们2者直接由Nginx将HTTP协议转换为Fastcgi协议传输给fastCGI进程处理 spawn-fcgi的编译与安装
wget http://download.lighttpd.net/spawn-fcgi/releases-1.6.x/spawn-fcgi-1.6.4.tar.gz tar zxf spawn-fcgi-1.6.4.tar.gz
cd ./configure
make
sudo make install
安装完成之后,spawn-fcgi程序被安装在了/usr/local/bin/目录下 spawn-fcgi的选项参数
通过“main spawn-fcgi”或者“spawn-fcgi -h”可以查看该程序的相关选项 常用的选项如下:
-f:指定调用 FastCGI 的进程的执行程序位置 -a:绑定到地址 addr -p:绑定到端口 port -s:绑定到 unix domain socket -C:指定产生的 FastCGI 的进程数,默认为 5(仅用于 PHP) -P:指定产生的进程的 PID 文件路径 -F:指定产生的 FastCGI 的进程数(C 的 CGI 用这个) -u、-g:使用什么身份(-u 用户、-g 用户组)运行,CentOS 下可 以使用 apache 用户,其他的根据情况配置,如 nobody、 www-data 等 七、echo回显应用程序 在fcgi源码包的example目录下有很多fcgi的演示案例,此处我们来使用echo这个程序案例 第一步
进入到源码包的example目录下,输入下面的命令启动fcgi程序 -a:表明这个cgi程序运行时的IP -p:表明这个cgi程序运行时的端口 -f:表明运行的cgi程序(路径别错了) spawn-fcgi -a 0.0.0.0 -p 8000 -f ./echo
第二步
拷贝Nginx的默认配置文件,取名为nginx_fastcgi.conf 。然后再配置文件的server模块中加入如下所示的内容: fastcgi_pass:FastCGI程序运行的地址,客户端访问到此location之后,就会将请求转发给FastCGI进行处理 fastcgi_index:如果请求的Fastcgi_index URI是以/echo结束的,该指令设置的文件会被附加到URI的后面并保存在变量$fastcig_script_name中 include指令:我们的配置文件使用到了FastCGI模块,那么就需要在内部引入Nginx给我们默认提供的fastcgi配置文件,这个配置文件名为fastcgi_params,存放在/usr/local/nginx/conf/目录下(注意路径,下面我给出的是相对路径)
sudo cp /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx_fastcgi.conf sudo vim /usr/local/nginx/conf/nginx_fastcgi.conf
worker_processes 4; events { worker_connections 1024; } http { server { listen 9000; location /echo { fastcgi_pass 0.0.0.0:8000; fastcgi_index echo; include fastcgi.conf; } } }
第三步
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx_fastcgi.conf
第四步
访问Nginx,注意我们的Nginx运行的端口为9000 。如下所示: 显示了我们FastCGI的进程ID为12358 还显示了fcgi相关的环境变量(上面介绍过了) 例如,我们通过下面的URL访问FastCGI, 那么其REQUEST_URI变量中就会显示请求的参数备注: 如果出现访问错误,应该是Nginx配置文件的缩进没写对,在shell中Tab缩进不是4字符八、编程演示案例 让Nginx运行FastCGI程序的工作原理大致为: 第一步:先编写一个FastCGI程序(例如名为demo.c),然后将其编译为一个可执行程序(例如编译名为demo) 第二步:使用spwanfcgi程序运行你这个FastCGI程序demo 第三步:编写Nginx配置文件,在配置文件的location模块中使用FasiCGI指令声明你这个FastCGI程序 第四步:客户端通过URI访问Nginx配置文件中对应FastCGI程序的location的URI,与CGI程序进行交互 第一步:
编写一个FastCGI程序,名为demo.c, 代码如下 #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>dongshao</h1>"); printf("Thank You cgi\n"); } }
编译上面的demo.c,编译的时候需要加上-lfcgi选项 gcc -o demo demo.c -lfcgi
第二步:
编写一个Nginx配置文件,存放在/usr/local/nginx/conf/目录下,名为my_fast_cgi.conf。 配置文件说明如下: Nginx的运行端口为9000 fastcgi_pass指令:FastCGI程序运行的地址,客户端访问到此location之后,就会将请求转发给FastCGI进行处理 fastcgi_index指令: 如果请求的Fastcgi_index URI是以/结束的, 该指令设置的文件会被附加到URI的后面并保存在变量$fastcig_script_name中 fastcgi_param指令:设置传递到FastCGI程序的参数和参数的值 include指令:我们的配置文件使用到了FastCGI模块,那么就需要在内部引入Nginx给我们默认提供的fastcgi配置文件,这个配置文件名为fastcgi_params,存放在/usr/local/nginx/conf/目录下(注意路径,下面我给出的是相对路径) sudo vim /usr/local/nginx/conf/my_fast_cgi.conf
worker_processes 4; events { worker_connections 1024; } http { server { listen 9000; # 只要URI以.cgi结尾,都会访问到这个location location ~ \.cgi { # 将Nginx的请求转发给fastcgi程序处理, fastcgi程序的地址为0.0.0.0:9001 fastcgi_pass 0.0.0.0:9001; # 如果客户端输入的URI是以/结尾的,那么默认访问/index.cgi程序 fastcgi_index index.cgi; fastcgi_param SCRIPT_FILENAME cgi$fastcgi_script_name; # 引入Nginx提供的fastcgi配置文件,注意路径 include fastcgi_params; } } }
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/my_fast_cgi.conf
第三步:
使用上面我们介绍的spwanfcgi程序运行这个cgi程序 -a:表明这个cgi程序运行时的IP -p:表明这个cgi程序运行时的端口 -f:表明运行的cgi程序(路径别错了) spawn-fcgi -a 0.0.0.0 -p 9001 -f ~/code/nginx/demo
第四步:
通过浏览器访问Nginx配置的cgi程序,URI为192.168.0.103:9000/demo.cgi 发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/157400.html原文链接:https://javaforall.cn