您的位置:首页 > 运维架构 > Nginx

Nginx + CGI/FastCGI + C/Cpp

2015-12-04 13:38 573 查看

Nginx+CGI/FastCGI+C/Cpp

2014-12-1911:05by吴秦,8859阅读,4评论,收藏,编辑

接着上篇《Nginx安装与使用》,本篇介绍CGI/FASTCGI的原理、及如何使用C/C++编写简单的CGI/FastCGI,最后将CGI/FASTCGI部署到nginx。内容大纲如下:

1.CGI

1.1.环境变量

1.2.标准输入

2.FastCGI

3.nginxcgi/fastcgi

3.1.nginx+fastcgi

3.1.1.spawn-fcgi

3.1.2.编写fastcgi应用程序

3.1.3.nginxfastcgi配置

3.2.nginx+cgi

3.2.1fastcgi-wrapper

3.2.2.nginxfcgiwrap配置

3.2.3.编写cgi应用程序

参考链接

1.CGI

通用网关接口CommonGatewayInterface/CGI描述了客户端和服务器程序之间传输数据的一种标准,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。CGI独立于任何语言的,CGI程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。Unixshellscript,Python,Ruby,PHP,perl,Tcl,C/C++,和VisualBasic都可以用来编写CGI程序。(http://www.dwz.cn/yFFgQ)

最初,CGI是在1993年由美国国家超级电脑应用中心(NCSA)为NCSAHTTPdWeb服务器开发的。这个Web服务器使用了UNIXshell环境变量来保存从Web服务器传递出去的参数,然后生成一个运行CGI的独立的进程。cgi的处理流程如下图所示:

lstep1.web服务器收到客户端(浏览器)的请求HttpRequest,启动CGI程序,并通过环境变量标准输入传递数据

lstep2.cgi进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等

lstep3.cgi程将处理结果通过标准输出标准错误,传递给web服务器

lstep4.web服务器收到cgi返回的结果,构建HttpResponse返回给客户端,并杀死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,可以有HTTPAcceptheader定义.

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

快速通用网关接口(FastCommonGatewayInterface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。FastCGI致力于减少Web服务器CGI程式之间互动的开销,从而使服务器可以同时处理更多的Web请求。与为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI进程管理器管理,而不是web服务器。(http://www.dwz.cn/yFMap)

当进来一个请求时,Web服务器把环境变量和这个页面请求通过一个unixdomainsocket(都位于同一物理服务器)或者一个IPSocket(FastCGI部署在其它物理服务器)传递给FastCGI进程。

lstep1.Web服务器启动时载入初始化FastCGI执行环境。例如IISISAPI、apachemod_fastcgi、nginxngx_http_fastcgi_module、lighttpdmod_fastcgi

lstep2.FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX域socket两种方式启动。

lstep3.当客户端请求到达Web服务器时,Web服务器将请求采用socket方式转发到FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web服务器将CGI环境变量和标准输入发送到FastCGI子进程。

lstep4.FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web服务器。当FastCGI子进程关闭连接时,请求便处理完成。

lstep5.FastCGI子进程接着等待并处理来自Web服务器的下一个连接。

由于FastCGI程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比CGI技术提高5倍以上。它还支持分布式的部署,即FastCGI程序可以在web服务器以外的主机上执行。

总结:CGI就是所谓的短生存期应用程序,FastCGI就是所谓的长生存期应用程序。FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute模式)。

3.nginxcgi/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。

lmake&makeinstall

如果遇到以下错误:“./autogen.sh:x:autoreconf:notfound”,因为没有安装automake工具,ubuntu用下面的命令安装好就可以了:sudoapt-getinstallautoconfautomakelibtool。

spawn-fcgi的帮助信息可以通过manspawn-fcgi或spawn-fcgi–h获得,下面是部分常用spawn-fcgi参数信息:

-f<fcgiapp>指定调用FastCGI的进程的执行程序位置

-a<addr>绑定到地址addr。

-p<port>绑定到端口port。

-s<path>绑定到unixdomainsocket

-C<childs>指定产生的FastCGI的进程数,默认为5。(仅用于PHP)

-P<path>指定产生的进程的PID文件路径。

-F<childs>指定产生的FastCGI的进程数(C的CGI用这个)

-u和-gFastCGI使用什么身份(-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。

lmake&makeinstall

如果编译提示一下错误:

fcgio.cpp:Indestructor'virtualfcgi_streambuf::~fcgi_streambuf()':

fcgio.cpp:50:error:'EOF'wasnotdeclaredinthisscope

fcgio.cpp:Inmemberfunction'virtualintfcgi_streambuf::overflow(int)':

fcgio.cpp:70:error:'EOF'wasnotdeclaredinthisscope

fcgio.cpp:75:error:'EOF'wasnotdeclaredinthisscope

fcgio.cpp:Inmemberfunction'virtualintfcgi_streambuf::sync()':

fcgio.cpp:86:error:'EOF'wasnotdeclaredinthisscope

fcgio.cpp:87:error:'EOF'wasnotdeclaredinthisscope

fcgio.cpp:Inmemberfunction'virtualintfcgi_streambuf::underflow()':

fcgio.cpp:113:error:'EOF'wasnotdeclaredinthisscope

make[2]:***[fcgio.lo]Error1

make[2]:Leavingdirectory`/root/downloads/fcgi-2.4.1-SNAP-0910052249/libfcgi'

make[1]:***[all-recursive]Error1

make[1]:Leavingdirectory`/root/downloads/fcgi-2.4.1-SNAP-0910052249'

make:***[all]Error2

解决办法:在/include/fcgio.h文件中加上#include<cstdio>,然后再编译安装就通过了。

如果提示找不到动态库,请在LD_LIBRARY_PATH或/etc/ld.so.conf中添加fcgi的安装路径,如/usr/local/lib,并执行ldconfig更新一下。

#include"fcgi_stdio.h"

#include<stdlib.h>

intmain(void)

{

intcount=0;

while(FCGI_Accept()>=0)

printf("Content-type:text/html\r\n"

"\r\n"

"<title>FastCGIHello!</title>"

"<h1>FastCGIHello!</h1>"

"Requestnumber%drunningonhost<i>%s</i>\n",

++count,getenv("SERVER_NAME"));

return0;

}


编译g++main.cpp-odemo–lfcgi,并将demo部署到/opt/nginx-1.7.7/cgi-bin/目录

通过spawn-fcgi启动c/c++编写好的fastcgi程序:/opt/nginx-1.7.7/sbin/spawn-fcgi-a127.0.0.1-p8081-f/opt/nginx-1.7.7/cgi-bin/demo

3.1.3.nginxfastcgi配置

关于nginx的几个配置文件解析,可以参阅《Nginx安装与使用》/article/4697809.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也比较简单。网上流传比较多的一个解决方案是,来自nginxwiki(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,进入解压目录

lautoreconf-i

l./configure

lmake&&makeinstall

启动fastcgi-wrapper:/opt/nginx-1.7.7/sbin/spawn-fcgi-f/usr/local/sbin/fcgiwrap-p8081

3.2.2.nginxfcgiwrap配置

在nginx.conf中增加下面的loaction配置块,这样所有的xxx.cgi请求都会走到fcgiwrap,然后fcgiwrap会执行cgi-bin目录下的cgi程序。

3.2.3.编写cgi应用程序

#include<stdio.h>

#include<stdlib.h>

intmain(void)

{

intcount=0;

printf("Content-type:text/html\r\n"

"\r\n"

"<title>CGIHello!</title>"

"<h1>CGIHello!</h1>"

"Requestnumber%drunningonhost<i>%s</i>\n",

++count,getenv("SERVER_NAME"));

return0;

}

tyler@ubuntu:~/ClionProjects/HelloFastCGI$g++cgi.cpp-ocgidemo-lfcgi

tyler@ubuntu:~/ClionProjects/HelloFastCGI$sudocpcgidemo/opt/nginx-1.7.7/cgi-bin/

注意图中的请求次数一直都是1,因为cgi的模式是fork-and-exec,每次都是一个新的进程。

参考链接

lCGI,http://www.dwz.cn/yFFgQ

lfastcgi,http://www.dwz.cn/yFMap

lspawn-fcgi,http://redmine.lighttpd.net/projects/spawn-fcgi/wiki

lfcgi,http://www.fastcgi.com/drupal/node/6?q=node/21

lfcgiwrap,https://github.com/gnosek/fcgiwrap.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: