FastCGI

CGI

什么是CGI

CGI(common gateway interface)通用网关接口描述了客户端和服务器程序之间传输数据的⼀种标准,可以让⼀个客户端,从⽹⻚浏览器向执⾏在⽹络服务器上的程序请求数据。
CGI独⽴于任何语⾔的,CGI程序可以⽤任何脚本语⾔或者是完全独⽴编程语⾔实现,只要这个语⾔可以在这个系统上运⾏。Unix shell script、Python、 Ruby、PHP、 perl、Tcl、 C/C++和 Visual Basic 都可以⽤来编写CGI程序。
最初,CGI是在1993年由美国国家超级电脑应⽤中⼼(NCSA)为NCSA HTTPd Web服务器开发的。这个Web服务器使⽤了UNIX shell环境变量来保存从Web服务器传递出去的参数,然后⽣成⼀个运⾏CGI的独⽴的进程。

CGI处理流程

web浏览器 web浏览器
| |
|1.http request |4.http response
| |
web服务器 web服务器
| |
|2.stdin |3.stdout,stderr
| |
启动CGI解析器 -> 加载配置 -> 连接其他服务器 -> 逻辑处理 -> 退出

  1. web服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量、标准输⼊传递 数据
  2. CGI进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等
  3. CGI进程将处理结果通过标准输出、标准错误,传递给web服务器
  4. web服务器收到CGI返回的结果,构建Http Response返回给客户端,并杀死CGI进程

特点

  1. web服务器和CGI通过环境变量、标准输入、标准输出、标准错误互相传递数据。
  2. web服务器接受到多少个请求就创建多少个CGI子进程,处理完请求后退出子进程,fork-and-execute。
  3. 每个⼦进程都需要启动⾃⼰的解释器、加 载配置,连接其他服务器等初始化⼯作,这是CGI进程性能低下的主要原因。当⽤户请求⾮常多的 时候,会占⽤⼤量的内存、cpu等资源,造成性能低下。
  4. CGI使外部程序与Web服务器之间交互成为可能。CGI程序运⾏在独⽴的进程中,并对每个Web请求建 ⽴⼀个进程,这种⽅法⾮常容易实现,但效率很差,难以扩展。⾯对⼤量请求,进程的⼤量建⽴和消亡 使操作系统性能⼤⼤下降。此外,由于地址空间⽆法共享,也限制了资源重⽤。

环境变量

GET请求它将数据打包放置在环境变量QUERY_STRING中,可以通过getenv(“QUERY_STRING”)函数获取数据。

环境变数含义
AUTH_TYPE存取认证类型
CONTENT_LENGTH由标准输⼊传递给CGI程序的数据⻓度,以bytes或字元数来计算
CONTENT_TYPE请求的MIME类型
GATEWAY_INTERFAC E服务器的CGI版本编号
HTTP_ACCEPT浏览器能直接接收的Content-types, 可以有HTTP Accept header定义
HTTP_USER_AGENT递交表单的浏览器的名称、版本和其他平台性的附加信息
HTTP_REFERER递交表单的⽂本的URL,不是所有的浏览器都发出这个信息,不要依赖它
PATH_INFO传递给CGI程序的路径信息
QUERY_STRING传递给CGI程序的请求参数,也就是⽤"?"隔开,添加在URL 后⾯的字串
REMOTE_ADDRclient端的host名称
REMOTE_HOSTclient端的IP位址
REMOTE_USERclient端送出来的使⽤者名称
REMOTE_METHODclient端发出请求的⽅法(如get、post)
SCRIPT_NAMECGI程序所在的虚拟路径,如/cgi-bin/echo
SERVER_NAMEserver的host名称或IP地址
SERVER_PORT收到request的server端⼝
SERVER_PROTOCOL所使⽤的通讯协定和版本编号
SERVER_SOFTWAREserver程序的名称和版本

标准输入

环境变量的⼤⼩是有⼀定的限制的,当需要传送的数据量⼤时,储存环境变量的空间可能会不⾜,造成数据接收不完全,甚⾄⽆法执⾏CGI程序。
因此后来⼜发展出另外⼀种⽅法:POST,也就是利⽤I/O重新导向的技巧,让CGI程序可以由stdin和stdout直接跟浏览器沟通。
当我们指定⽤这种⽅法传递请求的数据时,web服务器收到数据后会先放在⼀块输⼊缓冲区中,并且将数据的⼤⼩记录在CONTENT_LENGTH这个环境变量,然后调⽤CGI程序并将CGI程序的stdin指向这块缓冲区,于是我们就可以很顺利的通过stdin和环境变数CONTENT_LENGTH得到所有的信息,再没有信息⼤⼩的限制了。

FastCGI说明

什么是FastCGI

FastCGI(Fast Common Gateway Interface)快速通用网关接口,是通用网关接口CGI的改进。描述了客户端和服务器程序之间传输数据的⼀种标准。
FastCGI致⼒于减少Web服务器与CGI程式之间互动的开销,从⽽使服务器可以同时处理更多的Web请求。与为每个请求创建⼀个新的进程不同,FastCGI使⽤持续的进程来处理⼀连串的请求。这些进程由FastCGI进程管理器管理,⽽不是web服务器。
nginx服务⽀持FastCGI模式,能够快速⾼效地处理动态请求。⽽nginx对应的FastCGI模块为:ngx_http_fastcgi_module。
ngx_http_fastcgi_module模块允许将请求传递给FastCGI服务器。

处理流程

FastCIG进程管理器:启动CGI解析器 -> 加载配置 -> 连接其他服务器 -> 进行循环

web浏览器
|1
web服务器
|2
FastCGI执行环境
|3
进入循环 -> 逻辑处理 -> 退出 -> 循环等待
|4
web服务器
|5
web浏览器

  1. Web服务器启动时载⼊初始化FastCGI执⾏环境。例如IIS、ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi。
  2. FastCGI进程管理器⾃身初始化,启动多个CGI解释器进程并等待来⾃Web服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX域socket两种⽅式启动。
  3. 当客户端请求到达Web服务器时,Web服务器将请求采⽤socket⽅式转发FastCGI主进程,FastCGI主进程选择并连接到⼀个CGI解释器。Web服务器将CGI环境变量和标准输⼊发送到FastCGI⼦进程。
  4. FastCGI⼦进程完成处理后将标准输出和错误信息从同⼀socket连接返回Web服务器。当FastCGI⼦进程关闭连接时,请求便处理完成。
  5. FastCGI⼦进程接着等待并处理来⾃Web服务器的下⼀个连接。

FastCGI特点

  1. CGI是所谓的短⽣存期应⽤程序,FastCGI是所谓的⻓⽣存期应⽤程序。FastCGI像是⼀个常驻(long-live)型的CGI,它可以⼀直执⾏着,不会每次都要花费时间去fork⼀次(这是CGI最为⼈诟病的fork-and-execute模式)。
  2. FastCGI⼦进程完成处理后将标准输出和错误信息从同⼀socket连接返回Web服务器。

进程管理器spawn-fcgi

什么是spawn-fcgi

Nginx不能像Apache那样直接执⾏外部可执⾏程序,但Nginx可以作为代理服务器,将请求转发给后端服务器,这也是Nginx的主要作⽤之⼀。其中Nginx就⽀持FastCGI代理,接收客户端的请求,然后将请求转发给后端FastCGI进程。

由于FastCGI进程由FastCGI进程管理器管理,⽽不是Nginx。这样就需要⼀个FastCGI进程管理器,管理我们编写FastCGI程序。

spawn-fcgi是⼀个通⽤的FastCGI进程管理器,简单⼩巧,原先是属于lighttpd的⼀部分,后来由于使⽤⽐较⼴泛,所以就迁移出来作为独⽴项⽬。

spawn-fcgi使⽤pre-fork模型,功能主要是打开监听端⼝,绑定地址,然后fork-and-exec创建我们编写的FastCGI应⽤程序进程,退出完成⼯作。FastCGI应⽤程序初始化,然后进⼊死循环侦听socket的连接请求。

spawn-fcgi源码包下载地址:http://redmine.lighttpd.net/projects/spawn-fcgi/wiki

fcgi开发套件

使⽤C/C++编写FastCGI应⽤程序,可以使⽤FastCGI软件开发套件或者其它开发框架,如fcgi。
fcgi下载地址:wget https://fossies.org/linux/www/old/fcgi-2.4.0.tar.gz

FastCIG编译测试

nginx 设置

nginx需要在源码编译的时候加入fastcgi—module

location /test {
    fastcgi_pass 127.0.0.1:8001; 
    fastcgi_index test;
    include fastcgi.conf;
}

重新加载配置

/usr/local/nginx/sbin/nginx -s reload

测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fcgi_stdio.h"

int main(int argc, char *argv[])
{
    int count = 0;

    //阻塞等待并监听某个端⼝,等待Nginx将数据发过来

    while (FCGI_Accept() >= 0)
    {
        //如果想得到数据,需要从stdin去读,实际上从Nginx上去读
        //如果想上传数据,需要往stdout写,实际上是给Nginx写数据

        printf("Content-type: text/html\r\n");
        printf("\r\n");
        printf("<title>Fast CGI Hello!</title>");
        printf("<h1>Fast CGI Hello!</h1>");
        //SERVER_NAME:得到server的host名称
        printf("Request number %d running on host <i>%s</i>\n",
        ++count, getenv("SERVER_NAME"));
    }

    return 0;
}

编译启动

root@VM-0-16-ubuntu:/usr/local/nginx/sbin# lsof -i:8001
root@VM-0-16-ubuntu:/usr/local/nginx/sbin# cd /home/workplace/0voice_tuchuang/learn_test/
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# gcc fastcgi_src.c -o fastcgi_src -lfcgi
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# ldconfig
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# spawn-fcgi -a 127.0.0.1 -p 8001 -f ./fastcgi_src
spawn-fcgi: child spawned successfully: PID: 5926
root@VM-0-16-ubuntu:/home/workplace/0voice_tuchuang/learn_test# lsof -i:8001
COMMAND    PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
fastcgi_s 5926 root    0u  IPv4 15752309      0t0  TCP localhost:8001 (LISTEN)
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐