zoukankan      html  css  js  c++  java
  • Nginx + CGI/FastCGI + C/Cpp

    Nginx + CGI/FastCGI + C/Cpp

    2014-12-19 11:05 by 吴秦, 10329 阅读, 5 评论, 收藏编辑

    接着上篇《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应用程序

    参考链接

    1.CGI

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

    最初,CGI 是在 1993 年由美国国家超级电脑应用中心(NCSA)为 NCSA HTTPd Web 服务器开发的。这个 Web 服务器使用了 UNIXshell 环境变量 来保存从 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/html "

            " "

            "<title>FastCGI Hello!</title>"

            "<h1>FastCGI Hello!</h1>"

            "Request number %d running on host <i>%s</i> ",

            ++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/html "

            " "

            "<title>CGI Hello!</title>"

            "<h1>CGI Hello!</h1>"

            "Request number %d running on host <i>%s</i> ",

            ++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

  • 相关阅读:
    UVALive 7509 Dome and Steles
    HDU 5884 Sort
    Gym 101194H Great Cells
    HDU 5451 Best Solver
    HDU 5883 The Best Path
    HDU 5875 Function
    卡特兰数
    UVa 11729 Commando War 突击战
    UVa 11292 The Dragon of Loowater 勇者斗恶龙
    Spark Scala Flink版本对应关系
  • 原文地址:https://www.cnblogs.com/liangxiaofeng/p/5113178.html
Copyright © 2011-2022 走看看