历史来由:
早期的Web服务器,只能响应浏览器发来的HTTP静态资源的请求,并将存储在服务器中的静态资源返回给浏览器。随着Web技术的发展,逐渐出现了动态技术,但是Web服务器并不能够直接运行动态脚本,为了解决Web服务器与外部应用程序(CGI程序)之间数据互通,于是出现了CGI(Common Gateway Interface)通用网关接口。简单理解,可以认为CGI是Web服务器和运行其上的应用程序进行“交流”的一种约定。
简单地说CGI就是web服务器上的一种执行动态页面计算的外部逻辑计算扩展程序(webserver和cgi程序进行数据交互基于TCP协议)。
基本原理:
CGI的基本思路其实就是把标准输入(STDINT)、标准输出(STDOUT)、标准错误(STDERR)重定向到web server和cgi外部程序的tcp连接,进而直接从标准输入和进程环境变量中读取web server输入的数据,向stdout和stderror中写入参数,以此进行标准的数据交互。(CGI是Web服务器和一个独立的进程之间的协议,它会把HTTP请求Request的Header头设置成进程的环境变量,HTTP请求的Body正文设置成进程的标准输入,进程的标准输出设置为HTTP响应Response,包含Header头和Body正文。)
实现思路:
利用Linux下的dup、dup2两个api复制文件描述符,实现重定向,这里只介绍stdout和环境变量,只有stedin和stderror同理,就重复说了:
#include<unistd.h>
int dup(int oldfd);
int dup2(int oldfd,int newfd);
当调用dup函数时,内核在进程中创建一个新的文件描述符,此描述符是当前可用文件描述符的最小数值,这个文件描述符指向oldfd所拥有的文件表项。
dup2和dup的区别就是可以用newfd参数指定新描述符的数值,如果newfd已经打开,则先将其关闭。如果newfd等于oldfd,则dup2返回newfd, 而不关闭它。dup2函数返回的新文件描述符同样与参数oldfd共享同一文件表项。
APUE用另外一个种方法说明了这个问题:
实际上,调用dup(oldfd)等效于,fcntl(oldfd, F_DUPFD, 0)
而调用dup2(oldfd, newfd)等效于,close(oldfd);fcntl(oldfd, F_DUPFD, newfd);
server关键代码
int connfd = accept(sock,(struct sockaddr*)&client,&len);
if(connfd < 0)
printf("errno is:%d\n",errno);
else
{
close(STDOUT_FILENO);
dup(connfd);
printf("test cgi ret!\n");
}
client端代码
if((connect(sock,(struct sockaddr*)&client,sizeof(client)) != -1))
{
char buf[30];
printf("connect successful!\n");
if(recv(sock,buf,sizeof(buf),0) > 0)
cout<<buf<<endl;
}
运行结果:
connect successful!
test cgi ret!
关于cgi获取输入的另一个方式就是webserver设置给cgi进程的进程环境变量,其实就是在cgi中通过getenv函数获取环境变量对应的值:
while (FCGI_Accept() >= 0)
{
printf("Content-type: text/html\r\n");
printf("\r\n");
printf("<title>Fast CGI Hello!</title>");
printf("<h1>Fast CGI Hello!</h1>\n");
printf("Request number %d <br>\n", ++count);
printf("SERVER_NAME:%s <br>\n", getenv("SERVER_NAME"));
printf("SERVER_PORT:%s <br>\n", getenv("SERVER_PORT"));
printf("QUERY_STRING:%s <br>\n", getenv("QUERY_STRING"));
}
存在的优势和必要性:
必要性:早期的Web服务器,只能响应浏览器发来的HTTP静态资源的请求,并将存储在服务器中的静态资源返回给浏览器,无法实现动态计算页面,CGI通过外部扩展程序的方式,解决了http动态页面的问题
稳定性:
fastcgi 是以独立的进程池运行来 cgi,单独一个进程死掉,系统可以很轻易的丢弃,然后重新分配新的进程来运行逻辑
安全性:
fastcgi 和宿主的 server 完全独立, fastcgi 怎么 down 也不会把 server 搞垮
性能:
fastcgi 把动态逻辑的处理从 server 中分离出来, 大负荷的 IO 处理还是留给宿主 server, 这样宿主 server 可以一心一意作 IO,对于一个普通的动态网页来说, 逻辑处理可能只有一小部分, 大量的图片等静态 IO 处理完全不需要逻辑程序的参与
扩展性:
fastcgi 是一个中立的技术标准, 完全可以支持任何语言写的处理程序(php,java,python…)
语言无关性:
CGI 独立于任何语言的,CGI 程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。Unix shell script, Python, Ruby, PHP, perl, Tcl, C/C++, 和Visual Basic 都可以用来编写CGI 程序。
浏览器请求动态页面数据的整体基本流程:
- 通过 Internet 把用户请求送到 Web 服务器
2)Web 服务器接收到用户请求并交给 CGI 程序
3)CGI 程序把处理结果传送给 Web 服务器
4)Web 服务器把结果送回到用户
CGI 弊端
缺点: 一进程一请求的处理方式需要频繁的创建和销毁进程,导致web 服务器效率低、服务器系统资源占用大
fastcgi改进:
FastCGI
快速通用网关接口(FastCommonGatewayInterface/FastCGI)是通用网关接口(CGI)的改 进,描述了客户端和服务器程序之间传输数据的一种标准。FastCGI 致力于减少 Web 服务器 与 CGI 程式之间互动的开销,从而使服务器可以同时处理更多的 Web 请求。与为每个请求 创建一个新的进程不同,FastCGI 使用持续的进程来处理一连串的请求。这些进程由 FastCGI 进程管理器管理,而不是 web 服务器。
FastCGI 是什么?
FastCGI 是语言无关的、可伸缩架构的 CGI 开放扩展 其主要行为是将 CGI 解释器进程保持在内存中进行管理调度因此获得较高的性能
FastCGI 工作流程
- WebServer 启动时载入 FastCGI 进程管理器
- FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程 并等待来自 WebServer 的连 接
- 当客户端请求到达 WebServer 时,FastCGI 进程管理器选择并连接到一个 CGI 解释器 4) FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回 WebServer
FastCGI 协议
在 FastCGI 中,每一个 HTTP 请求(或者响应)消息都分为若干个记录(Record)进行传递, 每个 Record 又由头部(Header)和数据(Body)组成。
使用 Record 进行消息传递的好处:
• 多个请求的数据可以复用同一个连接进行传输,这样应用的实现就可以采用事件驱动的编 程模型或者多线程编程模型以提升效率;
• 同一个请求中的多个数据流的数据可以通过封装成不同记录的形式在同一个连接上传输, 例如 STDOUT 和 STDERR 两个输出流的数据可以通过同一个连接返回给 Web 服务器,而 不是不得不使用 2 个连接。
Record 结构体
typedef struct {
unsigned char version;
unsigned char type;
unsigned char requestIdB1;
unsigned char requestIdB0;
unsigned char contentLengthB1;
unsigned char contentLengthB0;
unsigned char paddingLength;
unsigned char reserved;
unsigned char contentData[contentLength];
unsigned char paddingData[paddingLength];
} FCGI_Record;
FastCGI 协议类型
FastCGI 是二进制连续传递的,定义了一个统一结构的消息头,用来读取每个消息的消息体, 方便消息包的切割。一般情况下, 最先发送的是 FCGI_BEGIN_REQUEST 类型的消息,然后是 FCGI_PARAMS 和 FCGI_STDIN 类型 的消息, 当 FastCGI 响应处理完后,将发送 FCGI_STDOUT 和 FCGI_STDERR 类型的消息,最后以 FCGI_END_REQUEST 表示请求的结束。 FCGI_BEGIN_REQUEST 和 FCGI_END_REQUEST 分别表示请求的开始和结束,与整个协议相关
#define FCGI_BEGIN_REQUEST 1
#define FCGI_ABORT_REQUEST 2
#define FCGI_END_REQUEST 3
#define FCGI_PARAMS 4
#define FCGI_STDIN 5
#define FCGI_STDOUT 6
#define FCGI_STDERR 7
#define FCGI_DATA 8
#define FCGI_GET_VALUES 9
#define FCGI_GET_VALUES_RESULT 10
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
**Web 服务器发送 FastCGI 请求时:**依次发送了 3 类 Record,类型分别为 BEGIN_REQUEST、PARAMS 和 STDIN FastCGI
**进程返回 FastCGI 响应时:**依次返回了 3 类 Record,类型分别为 STDOUT、STDERR、END_REQUEST
FastCGI 请求传递过程:
由 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 数据包格式
FastCGI 数据包两部分, 头部(header), 包体(body), 每个数据包都必须包含 header,body 可以 没有。header 为 8 个字节,body 必须为 8 的整数倍, 不是的话需要填充。
typedef struct {
unsigned char version;
unsigned char type;
unsigned char requestIdB1;
unsigned char requestIdB0;
unsigned char contentLengthB1;
unsigned char contentLengthB0;
unsigned char paddingLength;
unsigned char reserved;
}FCGI_Header;
数据包包体
FCGI_BEGIN_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;
FCGI_END_REQUEST 类型记录的 contentData 数据部分的结构:
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 配置参数和环境变量
FastCGI 配置参数
Nginx 基于模块 ngx_http_fastcgi_module 实现通过 fastcgi 协议将指定的客户端请求转发至 spawn-fcgi 处理。
fastcgi_pass address;
fastcgi_index name;
fastcgi_cache zone|off;
fastcgi_cache_key string;
fastcgi_cache_methods GET|HEAD|POST...;
fastcgi_hide_header field;
FastCGI 环境变量
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;#可自定义变量
5. FastCGI 进程管理器
5.1FastCGI 进程管理器
fastcgi 可使用 spawn-fcgi 或者 php-fpm 来管理 (fastcgi 进程管理器,有很多不同类型的) nginx 下 fastcgi 与服务器是分离的
5.2spawn-fcgi
5.2.1spawn-fcgi 作用?
• 相当一个代理工具
• 角色完成 nginx 和 fastcgi 之间的进程间通信
5.2.2 环境配置
• nginx 处理不了的指令, 交给 fastcgi 处理
• 数据需要转发
• 数据需要发送到指定的端口
• 处理一个指令 test
• url:http://192.168.52.139/test
location /test{
#配置 fastcgi 模块
fastcgi_pass 127.0.0.1:9001;
#IP:
#127.0.0.1/localhost/192.168.52.139
#端口:
#将要处理的数据发送到 9001 端口
#9001 端口对应一个进程, 该进程可以收到 nginx 发送过来的数据
include fastcgi.conf;
}
5.2.3spawn-fcgi 的使用
○ 编写一个 fcgi 程序 编译出来的程序名 test
○ spawn-fcgi-aIP-p 端口 -ffastcgi 程序 参数说明:
-a IP: 服务器 IP 地址
-p port: 服务器将数据发送到的端口
-f cgi 程序:spawn-fcgi 启动的可执行 fastcgi 程序
5.2.4fastCGI 协议、Spawn-fcgi、Nginx 三者关系
Nginx 是 web 服务器,只提供 HTTP 协议的输入和输出。
spawn-fcgi 服务器,只支持 Fastcgi 协议的输入和输出。
它们 2 者直接由 Nginx 将 HTTP 协议转换为 Fastcgi 协议传输给 fastCGI 进程处理
FastCGI、Spawn-fcgi、Nginx 三者关系
用户请求流程
6. http_fastcgi_module
nginx 服务支持 FastCGI 模式,能够快速高效地处理动态请求。 而 nginx 对应的 FastCGI 模块为 ngx_http_fastcgi_module。
ngx_http_fastcgi_module模块允许将请求传递给 FastCGI 服务器。 将 http 协议转为 fastcgi 协议
6.1ngx_http_fastcgi_module 模块配置
1. fastcgi_pass address;:指明反向代理的服务器
2.fastcgi_index# ;:定义 fastcgi 应用的默认主页;
3.fastcgi_paramparameter value [if_not_empty];:
3. fastcgi_cachezone | off;
4. fastcgi_cache_keystring;
5. fastcgi_cache_methods GET | HEAD | POST ...;
6. fastcgi_cache_min_usesnumber;
7. fastcgi_cache_use_staleerror | timeout | invalid_header | updating | http_500 | http_503 | http_403 |http_404 | off ...;
6.2ngx_http_fastcgi_module 更多信息
更多信息:http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html
6.3nginx 配置
○ 添加 location location /group1/M00
location /group1/M00 {
root /home/milo/fastdfs/storage/fastdfs0/data;
ngx_fastdfs_module;
}
○ 重新加载 nginx 的配置文件
6.3mod_fastdfs.conf 配置文件
一定参照当前存储节点 storage 的配置文件修改
○ log 日志目录 § base_path=/home/milo/fastDFS/storage
○ 追踪器的地址 § tracker_server=192.168.52.139:22122
○ 当前存储节点的端口 § storage_server_port=23000
○ 当前存储节点所属的组 § group_name=group1
○ 浏览器访问的时候, url 中是否包含组名 § url_have_group_name = true
○ 当前存储节点存储路径的个数 § store_path_count=1
○ 当前存储节点的存储路径
§ store_path0=/home/milo/fastDFS/storage/fastdfs0
□ 如果有多个, 需要全部写到配置文件中
® store_path1
® store_path2
○ 整个的 fastDFS 文件系统一共有多少个组 § group_count = 1
○ 每个组的信息 [group1] group_name=group1 storage_server_port=23000
store_path_count=1
store_path0=/home/milo/fastDFS/storage/fastdfs0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)