fastcgi
CGI
CGI是一个标准,它的英文Common Gateway Interface CGI , 所以从英文也可以看到它是C/S之间数据传输的一个标准。它主要用于浏览器向服务器发送一些参数,再由服务器上的程序进行运算然后返回结果的过程,在这个过程中CGI起着一个标准的作用,它规定了数据是如何交互的。有了CGI标准后,我们可以把写好的CGI程序放在任何一个符合CGI标准的Server上进行数据交互。
对于CGI标准来讲它要求我们必须通过0号文件描述符去读取,从1号文件描述符去写回数据。所以一般Server为了支持CGI标准都会做如下的操作:
- Server每次遇到CGI请求的时候会fork一个子进程去execve这个CGI程序。Server收到的参数通过环境变量传递,在fork后会对子进程的0、1号文件描述符(标准输入、标准输出)进行重定向,一般重定向为父进程生成的相应IPC的文件描述符。然后execve CGI程序,替换成功后会等待子进程的运算结果,收到结果后再把它返回给浏览器。这种方式就是符合了CGI标准,我们写的CGI程序只要符合从标准输入读取数据,从标准输出输出数据即可。
fastcgi
相比于cgi(fork+exec)的方式,fastcgi一般由一个cgi管理器进程或者服务器(Apache)去fork一个相应的cgi进程,这个cgi进程一直阻塞等待cgi的请求,一般由代理服务器(nginx)将HTTP请求转发给fastcgi进程,fastcgi接收到代理服务器的Socket的请求后,通过Socket进行数据交互。
这样缺点是浪费内核资源,优点是效率高,不用每次再次去fork+execve了。
实战
我们实战环境
- Fastcgi c++库
- nginx
- spawn-cgi (fastcgi 管理器)
spawn-fcgi
Nginx不能像Apache那样直接执行外部可执行程序,但是Nginx可以作为代理Server,将请求转发给后端FastCGI进程,FastCGI进程进行运算,然后把结果返回给Nginx,Nginx再把相应的内容返回给浏览器。
spawn-fcgi使用pre-fork模型,主要功能是打开监听端口,绑定地址,然后fork-and-exec创建FastCGI应用程序进程,创建完所有FastCGI进程后自动退出。FastCGI进程会进行初始化,然后进入死循环侦听socket的链接请求。
参数 | 含义 |
---|---|
-f | FastCGI程序的位置 |
-p | 生成的FastCGI程序绑定什么port |
-s | 绑定的unix domain socket |
-C | 产生的PHP进程数默认5(c++写的无视) |
-F | fork多少个FastCGI进程(C/C++) |
-P | 指定进程的默认路径(在Linux进程的默认路径也属于进程数据被记录在PCB中) |
-u/-g | 指定 euid / egid |
FastCGI c++ demo
echo demo
#include "fcgi_config.h"
#include <stdlib.h>
#include <unistd.h>
#include "fcgi_stdio.h"
static void printEnv(char *label,char **envp)
{
printf("%s:<br>\n<pre>\n",label);
for(;*envp != NULL;envp++){
printf("%s\n",*envp);
}
printf("</pre><p>");
}
int main()
{
char **initialEnv = environ;
int count = 0;
while(FCGI_Accept()>=0){
char *contentLength = getenv("CONTENT_LENGTH");
int len;
}
printf("Content-type: text/html\r\n"
"\r\n"
"<title>FastCGI echo</title>"
"<h1>FastCGI echo</h1>\n"
"Request number %d, Process ID: %d<p>\n", ++count, getpid());
if(contentLength != NULL){
len = strtol(contentLength,NULL,10);
}else{
len = 0;
}
if(len <= 0){
printf("No data from standard input.<p>\n");
}else{
int i,ch;
printf("Standard input:<br>\n<pre>\n");
for (i = 0; i < len; i++) {
if ((ch = getchar()) < 0) { printf("Error: Not enough bytes received on standard input<p>\n");
break;
}
putchar(ch);
}
printf("\n</pre><p>\n");
}
printEnv("Request environment",environ);
printEnv("Initial environment",initialEnv);
}
return 0;
}
HelloWorld demo
#include<stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include<fcgi_stdio.h>
#include<iostream>
using namespace std;
int main()
{
int count = 0;
int rt = 0;
FCGX_Init();
FCGX_Request request;
FCGX_InitRequest(&request,0,0);
while(true)
{
if((rt=FCGX_Accept_r(&request))<0)
{
cerr << "FCGX_Accept_r errno :" << rt ;
break;
}
FCGX_FPrintF(request.out,"Content-type: text/html\r\n"
"\r\n"
"<title>FastCGI Hello!</title>"
"<h1> FastCGI Hello!</h1>"
"<br /> Request count :%d "
"<br /> Process ID:%d ",count,getpid());
// 这里不用主动Finish 再次调用Accept的时候
//它会帮我们Finish上次的请求
}
return 0;
}
nginx配置
大家如果跑我的列子需要加nginx加入如下配置
location = /demo.cgi {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.cgi;
include fastcgi.conf;
}
实战图
hellocgi
echo cgi
FastCGIecho
Requestnumber2,ProcessID:2964
Nodatafromstandardinput.
Requestenvironment:
FCGI_ROLE=RESPONDER
SCRIPT_FILENAME=/usr/local/nginx/html/demo.cgi
QUERY_STRING=a&1
REQUEST_METHOD=GET
CONTENT_TYPE=
CONTENT_LENGTH=
SCRIPT_NAME=/demo.cgi
REQUEST_URI=/demo.cgi?a&1
DOCUMENT_URI=/demo.cgi
DOCUMENT_ROOT=/usr/local/nginx/html
SERVER_PROTOCOL=HTTP/1.1
GATEWAY_INTERFACE=CGI/1.1
SERVER_SOFTWARE=nginx/1.7.7
REMOTE_ADDR=127.0.0.1
REMOTE_PORT=59860
SERVER_ADDR=127.0.0.1
SERVER_PORT=80
SERVER_NAME=localhost
REDIRECT_STATUS=200
HTTP_HOST=127.0.0.1
HTTP_USER_AGENT=Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_LANGUAGE=en-GB,en;q=0.5
HTTP_ACCEPT_ENCODING=gzip, deflate
HTTP_CONNECTION=keep-alive
HTTP_UPGRADE_INSECURE_REQUESTS=1
Initialenvironment:
HOSTNAME=localhost.localdomain
SELINUX_ROLE_REQUESTED=
SHELL=/bin/bash
TERM=xterm
HISTSIZE=1000
SSH_CLIENT=192.168.248.1 50928 22
SELINUX_USE_CURRENT_RANGE=
QTDIR=/usr/lib64/qt-3.3
QTINC=/usr/lib64/qt-3.3/include
SSH_TTY=/dev/pts/0
USER=yutian
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.tbz=01;31:*.tbz2=01;31:*.bz=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
LD_LIBRARY_PATH=/usr/local/lib:
PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/yutian/bin:/usr/local/git/bin:/home/yutian/bin
MAIL=/var/spool/mail/yutian
_=./sbin/spawn-fcgi
PWD=/usr/local/nginx
LANG=en_US.UTF-8
SELINUX_LEVEL_REQUESTED=
HISTCONTROL=ignoredups
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
HOME=/home/yutian
SHLVL=1
LOGNAME=yutian
CVS_RSH=ssh
QTLIB=/usr/lib64/qt-3.3/lib
SSH_CONNECTION=192.168.248.1 50928 192.168.248.129 22
LESSOPEN=|/usr/bin/lesspipe.sh %s
PKG_CONFIG_PATH=/usr/local/lib/:/usr/local/lib/pkgconfig:/usr/local/lib/pkgconfig:/usr/local/lib/pkgconfig:
DISPLAY=localhost:10.0
G_BROKEN_FILENAMES=1
相关阅读
Spawn-FCGI安装 Nginx与fastcgi简单案例
获取spawn-fcgi编译安装包, 在 http://redmine.lighttpd.net/projects/spawn-fcgi/wiki 上可 以获取当 前最新的版本。tar -zxvf s