Nginx+CGIFastCGI+CCpp
接着上篇《》,本篇介绍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 应⽤程序
参考链接
1.CGI 最初,CGI 是在 1993 年由美国(NCSA )为 NCSA HTTPd Web 服务器开发的。这个 Web 服务器使⽤了 UNIX 来保存从 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 程序。因此后来⼜发展出另外⼀种⽅法:
当我们指定⽤这种⽅法传递请求的数据时,web 服务器收到数据后会先放在⼀块输⼊缓冲区中,并且将数据的⼤⼩记录在CONTENT_LENGTH 这个环境变数,然后调⽤CGI 总结:CGI 使外部程序与Web 服务器之间交互成为可能。CGI 程式运⾏在独⽴的进程中,并对每个Web 请求建⽴⼀个进程,这种⽅法⾮常容易实现,但效率很差,难以扩展。⾯对⼤量请求,进程的⼤量建⽴和消亡使操作系统性能⼤⼤下降。此外,由于地址空间⽆法共享,也限制了资源重⽤。
2.FastCGI
当进来⼀个请求时,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 模式)。
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解压缩ar.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++。
l解压缩ar.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/f中添加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/html\r\n"
"\r\n"
"<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配置
3.2. nginx + cgi
nginx 不能直接执⾏外部可执⾏程序,并且cgi是接收到请求时才会启动cgi进程,不像fastcgi会在⼀开就启动好,这样nginx天⽣是不⽀持 cgi 的。nginx 虽然不⽀持cgi,但它⽀持fastCGI。所以,我们可以
nginx和apache区别考虑使⽤fastcgi包装来⽀持 cgi。原理⼤致如下图所⽰:pre-fork⼏个通⽤的代理fastcgi程序——fastcgi-wrapper,fastcgi-wrapper启动执⾏cgi然后
将cgi的执⾏结果返回给nginx(fork-and-exec)。
3.2.1. fastcgi-wrapper
安装fcgiwrap:
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配置
在f中增加下⾯的loaction配置块,这样所有的i请求都会⾛到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/html\r\n"
"\r\n"
"<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,每次都是⼀个新的进程。
参考链接
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论