当前位置:首页 > 服务器技术 > nginx

Nginx + CGI/FastCGI + C/Cpp

接着上篇《 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-fcgispawn-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的进程数(CCGI用这个)

-u -g FastCGI使用什么身份(-u 用户 -g 用户组)运行,CentOS下可以使用apache用户,其他的根据情况配置,如nobodywww-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)!