FastCGI
CGI
什么是CGI
CGI(common gateway interface)通用网关接口描述了客户端和服务器程序之间传输数据的⼀种标准,可以让⼀个客户端,从⽹⻚浏览器向执⾏在⽹络服务器上的程序请求数据。
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处理流程
web浏览器 web浏览器
| |
|1.http request |4.http response
| |
web服务器 web服务器
| |
|2.stdin |3.stdout,stderr
| |
启动CGI解析器 -> 加载配置 -> 连接其他服务器 -> 逻辑处理 -> 退出
- web服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量、标准输⼊传递 数据
- CGI进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等
- CGI进程将处理结果通过标准输出、标准错误,传递给web服务器
- web服务器收到CGI返回的结果,构建Http Response返回给客户端,并杀死CGI进程
特点
- web服务器和CGI通过环境变量、标准输入、标准输出、标准错误互相传递数据。
- web服务器接受到多少个请求就创建多少个CGI子进程,处理完请求后退出子进程,fork-and-execute。
- 每个⼦进程都需要启动⾃⼰的解释器、加 载配置,连接其他服务器等初始化⼯作,这是CGI进程性能低下的主要原因。当⽤户请求⾮常多的 时候,会占⽤⼤量的内存、cpu等资源,造成性能低下。
- CGI使外部程序与Web服务器之间交互成为可能。CGI程序运⾏在独⽴的进程中,并对每个Web请求建 ⽴⼀个进程,这种⽅法⾮常容易实现,但效率很差,难以扩展。⾯对⼤量请求,进程的⼤量建⽴和消亡 使操作系统性能⼤⼤下降。此外,由于地址空间⽆法共享,也限制了资源重⽤。
环境变量
GET请求它将数据打包放置在环境变量QUERY_STRING中,可以通过getenv(“QUERY_STRING”)函数获取数据。
环境变数 | 含义 |
---|---|
AUTH_TYPE | 存取认证类型 |
CONTENT_LENGTH | 由标准输⼊传递给CGI程序的数据⻓度,以bytes或字元数来计算 |
CONTENT_TYPE | 请求的MIME类型 |
GATEWAY_INTERFAC E | 服务器的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得到所有的信息,再没有信息⼤⼩的限制了。
FastCGI说明
什么是FastCGI
FastCGI(Fast Common Gateway Interface)快速通用网关接口,是通用网关接口CGI的改进。描述了客户端和服务器程序之间传输数据的⼀种标准。
FastCGI致⼒于减少Web服务器与CGI程式之间互动的开销,从⽽使服务器可以同时处理更多的Web请求。与为每个请求创建⼀个新的进程不同,FastCGI使⽤持续的进程来处理⼀连串的请求。这些进程由FastCGI进程管理器管理,⽽不是web服务器。
nginx服务⽀持FastCGI模式,能够快速⾼效地处理动态请求。⽽nginx对应的FastCGI模块为:ngx_http_fastcgi_module。
ngx_http_fastcgi_module模块允许将请求传递给FastCGI服务器。
处理流程
FastCIG进程管理器:启动CGI解析器 -> 加载配置 -> 连接其他服务器 -> 进行循环
web浏览器
|1
web服务器
|2
FastCGI执行环境
|3
进入循环 -> 逻辑处理 -> 退出 -> 循环等待
|4
web服务器
|5
web浏览器
- Web服务器启动时载⼊初始化FastCGI执⾏环境。例如IIS、ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi。
- FastCGI进程管理器⾃身初始化,启动多个CGI解释器进程并等待来⾃Web服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX域socket两种⽅式启动。
- 当客户端请求到达Web服务器时,Web服务器将请求采⽤socket⽅式转发FastCGI主进程,FastCGI主进程选择并连接到⼀个CGI解释器。Web服务器将CGI环境变量和标准输⼊发送到FastCGI⼦进程。
- FastCGI⼦进程完成处理后将标准输出和错误信息从同⼀socket连接返回Web服务器。当FastCGI⼦进程关闭连接时,请求便处理完成。
- FastCGI⼦进程接着等待并处理来⾃Web服务器的下⼀个连接。
FastCGI特点
- CGI是所谓的短⽣存期应⽤程序,FastCGI是所谓的⻓⽣存期应⽤程序。FastCGI像是⼀个常驻(long-live)型的CGI,它可以⼀直执⾏着,不会每次都要花费时间去fork⼀次(这是CGI最为⼈诟病的fork-and-execute模式)。
- FastCGI⼦进程完成处理后将标准输出和错误信息从同⼀socket连接返回Web服务器。
进程管理器spawn-fcgi
什么是spawn-fcgi
Nginx不能像Apache那样直接执⾏外部可执⾏程序,但Nginx可以作为代理服务器,将请求转发给后端服务器,这也是Nginx的主要作⽤之⼀。其中Nginx就⽀持FastCGI代理,接收客户端的请求,然后将请求转发给后端FastCGI进程。
由于FastCGI进程由FastCGI进程管理器管理,⽽不是Nginx。这样就需要⼀个FastCGI进程管理器,管理我们编写FastCGI程序。
spawn-fcgi是⼀个通⽤的FastCGI进程管理器,简单⼩巧,原先是属于lighttpd的⼀部分,后来由于使⽤⽐较⼴泛,所以就迁移出来作为独⽴项⽬。
spawn-fcgi使⽤pre-fork模型,功能主要是打开监听端⼝,绑定地址,然后fork-and-exec创建我们编写的FastCGI应⽤程序进程,退出完成⼯作。FastCGI应⽤程序初始化,然后进⼊死循环侦听socket的连接请求。
spawn-fcgi源码包下载地址:http://redmine.lighttpd.net/projects/spawn-fcgi/wiki
fcgi开发套件
使⽤C/C++编写FastCGI应⽤程序,可以使⽤FastCGI软件开发套件或者其它开发框架,如fcgi。
fcgi下载地址:wget https://fossies.org/linux/www/old/fcgi-2.4.0.tar.gz
FastCIG编译测试
nginx 设置
nginx需要在源码编译的时候加入fastcgi—module
location /test {
fastcgi_pass 127.0.0.1:8001;
fastcgi_index test;
include fastcgi.conf;
}
重新加载配置
/usr/local/nginx/sbin/nginx -s reload
测试代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fcgi_stdio.h"
int main(int argc, char *argv[])
{
int count = 0;
//阻塞等待并监听某个端⼝,等待Nginx将数据发过来
while (FCGI_Accept() >= 0)
{
//如果想得到数据,需要从stdin去读,实际上从Nginx上去读
//如果想上传数据,需要往stdout写,实际上是给Nginx写数据
printf("Content-type: text/html\r\n");
printf("\r\n");
printf("<title>Fast CGI Hello!</title>");
printf("<h1>Fast CGI Hello!</h1>");
//SERVER_NAME:得到server的host名称
printf("Request number %d running on host <i>%s</i>\n",
++count, getenv("SERVER_NAME"));
}
return 0;
}
编译启动
root@VM-0-16-ubuntu:/usr/local/nginx/sbin# lsof -i:8001
root@VM-0-16-ubuntu:/usr/local/nginx/sbin# cd /home/workplace/0voice_tuchuang/learn_test/
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# gcc fastcgi_src.c -o fastcgi_src -lfcgi
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# ldconfig
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# spawn-fcgi -a 127.0.0.1 -p 8001 -f ./fastcgi_src
spawn-fcgi: child spawned successfully: PID: 5926
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# lsof -i:8001
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
fastcgi_s 5926 root 0u IPv4 15752309 0t0 TCP localhost:8001 (LISTEN)