接着上篇《 Nginx安装与使用 》,本篇介绍CGI/FASTCGI的原理、及如何使用C/C++编写简单的CGI/FastCGI,最后将CGI/FASTCGI部署到nginx。内容大纲如下:
1. CGI
1.1. 环境变量
1.2. 标准输入
2. FastCGI
3. nginx cgi/fastcgi
3.1. nginx + fastcgi
3.1.1. spawn-fcgi
3.1.2. 编写fastcgi应用程序
3.1.3. nginx fastcgi配置
3.2. nginx + cgi
3.2.1 fastcgi-wrapper
3.2.2. nginx fcgiwrap配置
3.2.3. 编写cgi应用程序
参考链接
<?xml:namespace prefix = o />
1.CGI
通用网关接口
(
C
ommon Gateway Interface/CGI
)描述了客户端和服务器程序之间传输数据的一种标准,可以让一个客户端,从
网页浏览器向执行在网络服务器上的程序请求数据。
CGI
独立于任何语言的,
CGI
程序可以
用任何
脚本语言
或者是完全独立
编程语言
实现,只要这个语言可以在这个系统上运行。
Unix shell
script,
Python
,
Ruby
,
PHP
, perl,
Tcl
,
C
/
C++
,
和
Visual Basic
都可以用来编写
CGI
程序。
(
http://www.dwz.cn/yFFgQ
)
最初,
CGI
是在
1993
年由美国
国家超级电脑应用中心
(
NCSA
)为
NCSA HTTPd
Web
服务器开发的。这个
Web
服务器使用了
UNIX
shell
环境变量
来保存从
Web
服务器传递出去的参数,然后生成一个运行
CGI
的独立的
进程
。
cgi
的处理流程如下图所示:
l
step1.
web
服务器收到客户端(浏览器)的请求
Http Request
,启动
CGI
程序,并通过环境变量、标准输入传递数据
l
step2. cgi
进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等
l
step3. cgi
程将处理结果通过标准输出、标准错误,传递给
web
服务器
l
step4. web
服务器收到
cgi
返回的结果,构建
Http Response
返回给客户端,并杀死
cgi
进程
web
服务器与
cgi
通过环境变量、标准输入、标准输出、标准错误互相传递数据。
1.1.
环境变量
GET
请求,它将数据打包放置在环境变量
QUERY_STRING
中,
CGI
从环境变量
QUERY_STRING
中获取数据。常见的环境变量如下表所示:
|
环境变数
|
内容
|
|
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
程序的名称和版本。
|
1.2.
标准输入
环境变量的大小是有一定的限制的,当需要传送的数据量大时,储存环境变量的空间可能会不足,造成数据接收不完全,甚至无法执行
CGI
程序。因此后来又发展出另外一种方法:
POST
,也就是利用
I/O
重新导向的技巧,让
CGI
程序可以由
STDIN
和
STDOUT
直接跟浏览器沟通。
当我们指定用这种方法传递请求的数据时,
web
服务器收到数据后会先放在一块输入缓冲区中,并且将数据的大小记录在
CONTENT_LENGTH
这个环境变数,然后调用
CGI
程式并将
CGI
程序的
STDIN
指向这块缓冲区,于是我们就可以很顺利的通过
STDIN
和环境变数
CONTENT_LENGTH
得到所有的资料,再没有资料大小的限制了。
总结:
CGI
使外部程序与
Web
服务器之间交互成为可能。
CGI
程式运行在独立的进程中,并对每个
Web
请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。
2.
FastCGI
快速通用网关接口(
Fast Common Gateway Interface
/
FastCGI
)是
通用网关接口
(
CGI
)的改进,描述了客户端和服务器程序之间传输数据的一种标准。
FastCGI
致力于减少
Web服务器
与
CGI
程式
之间互动的开销,从而使
服务器
可以同时处理更多的
Web
请求
。与为每个请求创建一个新的进程不同,
FastCGI
使用持续的进程来处理一连串的请求。这些进程由
FastCGI
进程管理器管理,而不是
web
服务器。
(http://www.dwz.cn/yFMap)
当进来一个请求时,
Web
服务器把环境变量和这个页面请求通过一个
unix domain socket(
都位于同一物理服务器)或者一个
IP Socket
(
FastCGI
部署在其它物理服务器)传递给
FastCGI
进程。
l
step1. Web
服务器启动时载入初始化
FastCGI
执行环境
。
例如
IIS ISAPI
、
apache mod_fastcgi
、
nginx ngx_http_fastcgi_module
、
lighttpd mod_fastcgi
l
step2. FastCGI
进程管理器自身初始化,启动多个
CGI
解释器进程并等待来自
Web
服务器的连接。启动
FastCGI
进程时,可以配置以
ip
和
UNIX
域
socket
两种方式启动。
l
step3.
当客户端请求到达
Web
服务器时,
Web
服务器将请求采用
socket
方式转发到
FastCGI
主进程,
FastCGI
主进程选择并连接到一个
CGI
解释器。
Web
服务器将
CGI
环境变量和标准输入发送到
FastCGI
子进程。
l
step4. FastCGI
子进程完成处理后将标准输出和错误信息从同一
socket
连接返回
Web
服务器。当
FastCGI
子进程关闭连接时,请求便处理完成。
l
step5. FastCGI
子进程接着等待并处理来自
Web
服务器的下一个连接。
由于
FastCGI
程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比
CGI
技术提高
5
倍以上。它还支持分布式的部署,
即
FastCGI
程序可以在
web
服务器以外的主机上执行。
总结:
CGI
就是所谓的短生存期应用程序,
FastCGI
就是所谓的长生存期应用程序。
FastCGI
像是一个常驻
(long-live)
型的
CGI
,它可以一直执行着,不会每次都要花费时间去
fork
一次
(
这是
CGI
最为人诟病的
fork-and-execute
模式
)
。
3.
nginx cgi/fastcgi
nginx
不能像
apache
那样直接执行外部可执行程序,但
nginx
可以作为代理服务器,将请求转发给后端服务器,这也是
nginx
的主要作用之一。其中
nginx
就支持
FastCGI
代理,接收客户端的请求,然后将请求转发给后端
fastcgi
进程。
下面介绍如何使用
C/C++
编写
cgi/fastcgi
,并部署到
nginx
中。
3.1.
nginx + fastcgi
通过前面的介绍知道,
fastcgi
进程由
FastCGI
进程管理器管理,而不是
nginx
。这样就需要一个
FastCGI
管理,管理我们编写
fastcgi
程序。本文使用
spawn-fcgi
作为
FastCGI
进程管理器。
3.1.1.
spawn-fcgi
spawn-fcgi
是一个通用的
FastCGI
进程
管理器,
简单小巧,原先是属于
lighttpd
的一部分,后来由于使用比较广泛,所以就迁移出来作为独立项目了。
spawn-fcgi
使用
pre-fork
模型,功能主要是打开监听端口,绑定地址,然后
fork-and-exec
创建我们编写的
fastcgi
应用程序进程,退出完成工作。
fastcgi
应用程序初始化,然后进入死循环侦听
socket
的连接请求。
安装
spawn-fcgi
:
l
获取
spawn-fcgi
编译安装包,在
http://redmine.lighttpd.net/projects/spawn-fcgi/wiki
上可以获取当前最新的版本。
l
解压缩
spawn-fcgi-x.x.x.tar.gz
包。
l
进入解压缩目录,执行
./configure
。
l
make & make install
如果遇到以下错误:“
./autogen.sh: x: autoreconf: not found
”,因为没有安装
automake
工具,
ubuntu
用下面的命令安装好就可以了:
sudo apt-get install autoconf automake libtool
。
spawn-fcgi
的帮助信息可以通过man spawn-fcgi或spawn-fcgi –h获得,下面是部分常用spawn-fcgi参数信息:
|
-f <fcgiapp>
指定调用FastCGI的进程的执行程序位置
-a <addr>
绑定到地址addr。
-p <port>
绑定到端口port。
-s <path>
绑定到unix domain socket
-C <childs>
指定产生的FastCGI的进程数,默认为5。(仅用于PHP)
-P <path>
指定产生的进程的PID文件路径。
-F <childs>
指定产生的FastCGI的进程数(C的CGI用这个)
-u
和-g FastCGI使用什么身份(-u 用户 -g 用户组)运行,CentOS下可以使用apache用户,其他的根据情况配置,如nobody、www-data等。
|
3.1.2.
编写
fastcgi
应用程序
使用
C/C++
编写
fastcgi
应用程序,可以使用
FastCGI
软件开发套件或者其它开发框架,如
fastcgi++
。
本文使用
FastCGI
软件开发套件
——fcgi
(
http://www.fastcgi.com/drupal/node/6?q=node/21
),通过此套件可以轻松编写
fastcgi
应用程序,安装
fcgi
:
l
获取
fcgi
编译安装包,在
http://www.fastcgi.com/drupal/node/5
上可以获取当前最新的版本。
l
解压缩
fcgi-x.x.x.tar.gz
包。
l
进入解压缩目录,执行
./configure
。
l
make & make install
如果编译提示一下错误:
fcgio.cpp: In destructor ‘virtual fcgi_streambuf::~fcgi_streambuf()‘:
fcgio.cpp:50: error: ‘EOF‘ was not declared in this scope
fcgio.cpp: In member function ‘virtual int fcgi_streambuf::overflow(int)‘:
fcgio.cpp:70: error: ‘EOF‘ was not declared in this scope
fcgio.cpp:75: error: ‘EOF‘ was not declared in this scope
fcgio.cpp: In member function ‘virtual int fcgi_streambuf::sync()‘:
fcgio.cpp:86: error: ‘EOF‘ was not declared in this scope
fcgio.cpp:87: error: ‘EOF‘ was not declared in this scope
fcgio.cpp: In member function ‘virtual int fcgi_streambuf::underflow()‘:
fcgio.cpp:113: error: ‘EOF‘ was not declared in this scope
make[2]: *** [fcgio.lo] Error 1
make[2]: Leaving directory `/root/downloads/fcgi-2.4.1-SNAP-0910052249/libfcgi‘
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/root/downloads/fcgi-2.4.1-SNAP-0910052249‘
make: *** [all] Error 2
解决办法:在
/include/fcgio.h
文件中加上
#include <cstdio>
,然后再编译安装就通过了。
如果提示找不到动态库,请在
LD_LIBRARY_PATH
或
/etc/ld.so.conf
中添加
fcgi
的安装路径,如
/usr/local/lib
,并执行
ldconfig
更新一下。
#include
"fcgi_stdio.h"
#include
<stdlib.h>
int
main
(
void
)
{
int
count
= 0;
while
(FCGI_Accept() >= 0)
printf
(
"Content-type: text/htmlrn"
"rn"
"<title>FastCGI Hello!</title>"
"<h1>FastCGI Hello!</h1>"
"Request number %d running on host <i>%s</i>n"
,
++
count
,
getenv
(
"SERVER_NAME"
));
return
0;
}
编译
g++ main.cpp -o demo –lfcgi
,并将
demo
部署到
/opt/nginx-1.7.7/cgi-bin/
目录
通过
spawn-fcgi
启动
c/c++
编写好的
fastcgi
程序:
/opt/nginx-1.7.7/sbin/spawn-fcgi -a 127.0.0.1 -p 8081 -f /opt/nginx-1.7.7/cgi-bin/demo
3.1.3.
nginx fastcgi
配置
关于
nginx
的几个配置文件解析,可以参阅《
Nginx安装与使用
》
http://www.cnblogs.com/skynet/p/4146083.html
,在上篇的
nginx.conf
基础上增加下面的
fastcgi
配置。
这样
nginx
收到
http://localhost/demo.cgi
请求时,会匹配到
location = /demo.cgi
块,将请求传到后端的
fastcgi
应用程序处理。如下如所示:(注意其中
number
为
80
,是因为我请求了
80
次)
3.2.
nginx + cgi
nginx
不能直接执行外部可执行程序,并且
cgi
是接收到请求时才会启动
cgi
进程,不像
fastcgi
会在一开就启动好,这样
nginx
天生是不支持
cgi
的。
nginx
虽然不支持
cgi
,但它支持
fastCGI
。所以,我们可以考虑使用
fastcgi
包装来支持
cgi
。原理大致如下图所示:
pre-fork
几个通用的代理
fastcgi
程序——
fastcgi-wrapper
,
fastcgi-wrapper
启动执行
cgi
然后将
cgi
的执行结果返回给
nginx
(
fork-and-exec
)。
明白原理之后,编写一个
fastcgi-warpper
也比较简单。网上流传比较多的一个解决方案是,来自
nginx wiki
(
http://wiki.nginx.org/SimpleCGI
)上的使用
perl
的
fastcgi
包装脚本
cgiwrap-fcgi.pl
。但我对
perl
不是很感冒,下面给出一个
C/C++
写的
fastcgi-wrapper
。
3.2.1.
fastcgi-wrapper
其实编写
C/C++
的
fastcgi-wrapper
,就是写一个
C/C++
的
fastcgi
,步骤和原理跟前面的小节(
nginx+fastcgi
)一样。
github
上已经有人开源了,
C
写的
fastcgi-wrapper
:
https://github.com/gnosek/fcgiwrap
。
安装
fcgiwrap
:
l
下载(
https://github.com/gnosek/fcgiwrap.git
)
l
解压缩
fcgiwrap
,进入解压目录
l
autoreconf -i
l
./configure
l
make && make install
启动
fastcgi-wrapper
:
/opt/nginx-1.7.7/sbin/spawn-fcgi
-f /usr/local/sbin/fcgiwrap -p 8081
3.2.2.
nginx
fcgiwrap
配置
在
nginx.conf
中增加下面的
loaction
配置块,这样所有的
xxx.cgi
请求都会走到
fcgiwrap
,然后
fcgiwrap
会执行
cgi-bin
目录下的
cgi
程序。
3.2.3.
编写
cgi
应用程序
#include
<stdio.h>
#include
<stdlib.h>
int
main
(
void
)
{
int
count
= 0;
printf
(
"Content-type: text/htmlrn"
"rn"
"<title>CGI Hello!</title>"
"<h1>CGI Hello!</h1>"
"Request number %d running on host <i>%s</i>n"
,
++
count
,
getenv
(
"SERVER_NAME"
));
return
0;
}
tyler@ubuntu:~/ClionProjects/HelloFastCGI$ g++ cgi.cpp -o cgidemo -lfcgi
tyler@ubuntu:~/ClionProjects/HelloFastCGI$ sudo cp cgidemo /opt/nginx-1.7.7/cgi-bin/
注意图中的请求次数一直都是
1
,因为
cgi
的模式是
fork-and-exec
,每次都是一个新的进程。
参考链接
l
CGI, http://www.dwz.cn/yFFgQ
l
fastcgi, http://www.dwz.cn/yFMap
l
spawn-fcgi, http://redmine.lighttpd.net/projects/spawn-fcgi/wiki
l
fcgi, http://www.fastcgi.com/drupal/node/6?q=node/21
l
fcgiwrap, https://github.com/gnosek/fcgiwrap.git
原文:http://www.cnblogs.com/skynet/p/4173450.html
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!