FastCGI编程介绍
CGI(Common Gateway Interface)公共网关接口,是HTTP服务器与其他程序通信的工具。FastCGI是一个long-live型的CGI,支持分布式计算,它将CGI解释器进程保持在内存中并因此获得较高的性能。FastCGI工作方式是接受Web服务器的请求,以HTTP Request的方式进行响应,实现了应用程序与Web服务器的分离。它的使用需要在Web服务器中安装支持组件
CGI(Common Gateway Interface)公共网关接口,是HTTP服务器与其他程序通信的工具。FastCGI是一个long-live型的CGI,支持分布式计算,它将CGI解释器进程保持在内存中并因此获得较高的性能。
FastCGI工作方式是接受Web服务器的请求,以HTTP Request的方式进行响应,实现了应用程序与Web服务器的分离。它的使用需要在Web服务器中安装支持组件,目前支持Apache、Nginx、IIS、Lighttpd等。
优点有:
- 稳定性,fastcgi是以独立的进程池运行来cgi,单独一个进程死掉,系统可以很轻易的丢弃,然后重新分配新的进程来运行逻辑
- 安全性,fastcgi和宿主的server完全独立,fastcgi怎么down也不会把server搞垮
- 性能, fastcgi把动态逻辑的处理从server中分离出来, 大负荷的IO处理还是留给宿主server, 这样宿主server可以一心一意作IO,对于一个普通的动态网页来说,,逻辑处理可能只有一小部分,大量的图片等静态IO处理完全不需要逻辑程序的参与
- 扩展性,fastcgi是一个中立的技术标准,完全可以支持任何语言写的处理程序(php、java、python、C++)
FastCGI编程包括四部分:初始化编码、接收请求循环、响应内容、响应结束循环。
- FCGX_Request request;
- FCGX_Init();
- int sock_fd = FCGX_OpenSocket("10.3.17.75:8003",100);
- FCGX_InitRequest(&request, sock_fd, 0);
- while (FCGX_Accept_r(&request) >= 0) {
- //get param 1
- map<string,string> param_map;
- for(int i = 0; request.envp[i]; ++i) {
- string s = request.envp[i];
- size_t pos = s.find_first_of('=');
- if (pos > 0 && pos < s.size() - 1) {
- param_map.insert(make_pair(s.substr(0,pos), s.substr(pos+1)));
- }
- }
- //or 2
- char * clenstr = FCGX_GetParam("CONTENT_LENGTH", request.envp);
- //do something
- FCGX_Stream* fcgi_out = request.out;
- string output="test";
- FCGX_PutS(output.c_str(), fcgi_out);
- //finish
- FCGX_Finish_r(&request);
- }
0.背景
在项目中加入了等待通讯的内容,所以原来单个请求处理时间增加了。单线程处理的fcgi就会浪费CPU和用户时间,所以需要多线程来处理,减少用户排队时间。
将处理用户请求的部分从单线程变为多线程,需要大概了解改动会不会影响性能。
得到的结论是:多线程和单线程在执行的流程和使用方法几乎一样,所以多线程不会带来额外的负担。
1.单线程的处理步骤
1.1一个简单的单线程fcgi请求
- #include <fcgi_stdio.h>
- void main(void)
- {
- int count = 0;
- while(FCGI_Accept() >= 0) {
- printf("Content-type: text/html\r\n");
- printf("\r\n");
- printf("Hello world!<br>\r\n");
- printf("Request number %d.", count++);
- }
- exit(0);
- }
1.2进入FCGI_Accept。
进入这个 FCGI_Accept() 方法里面,在文件fcgi_stdio.c里。
- int FCGI_Accept(void)
- {
- //变量表示是否接收请求。默认为Fasle,不接收请求
- if(!acceptCalled) {
- //判断是否为cgi,变量为全局静态的,下次还会用。
- isCGI = FCGX_IsCGI();
- //状态改为接收请求。
- acceptCalled = TRUE;
- //请求的收尾,将数值清空赋初值。
- atexit(&FCGI_Finish);
- } else if(isCGI) {
- //不是第一次请求,并且是cgi程序。
- return(EOF);
- }
- if(isCGI) {
- //cgi的初始赋值操作,不关心。
- ...
- } else {
- FCGX_Stream *in, *out, *error;
- //char** 字符串数组。
- FCGX_ParamArray envp;
- //接受请求,这个方法下面介绍
- int acceptResult = FCGX_Accept(&in, &out, &error, &envp);
- //接收失败,返回<0,这也是为什么在循环上判断是 while(FCGI_Accept() >= 0)
- if(acceptResult < 0) {
- return acceptResult;
- }
- //将得到的数据赋值给对应的输出,输入,data。
- FCGI_stdin->stdio_stream = NULL;
- FCGI_stdin->fcgx_stream = in;
- FCGI_stdout->stdio_stream = NULL;
- FCGI_stdout->fcgx_stream = out;
- FCGI_stderr->stdio_stream = NULL;
- FCGI_stderr->fcgx_stream = error;
- environ = envp;
- }
- //结束
- return 0;
- }
1.3 FCGX_Accept(&in, &out, &error, &envp)
等待接收请求的方法,在fcgiaoo.c里。
- static FCGX_Request the_request;
- int FCGX_Accept(FCGX_Stream **in,FCGX_Stream **out,FCGX_Stream **err,FCGX_ParamArray *envp)
- {
- int rc;//定义返回的变量。
- //是否初始化过。
- if (! libInitialized) {
- rc = FCGX_Init();
- if (rc) {
- return rc;
- }
- }
- //接收数据,下面介绍
- rc = FCGX_Accept_r(&the_request);
- //给对应流和数据赋值。
- *in = the_request.in;
- *out = the_request.out;
- *err = the_request.err;
- *envp = the_request.envp;
- return rc;
- }
1.4 FCGX_Accept_r(),同在fcgiapp.c里面;
- /*
- *----------------------------------------------------------------------
- *
- * FCGX_Accept_r --
- *
- * 从http server 接收一个新的请求
- * Results:
- * 正确返回0,错误返回-1.
- * Side effects:
- *
- * 通过FCGX_Accept完成请求的接收,创建input,output等流并且各自分配给in
- * ,out,err.创建参数数据从FCGX_GetParam中取出,并且给envp。
- * 不要保存指针和字符串,他们会在下次请求中的FcGX_Finish中被释放。
- *----------------------------------------------------------------------
- */
- int FCGX_Accept_r(FCGX_Request *reqDataPtr)
- {
- if (!libInitialized) {
- return -9998;
- }
- //将当前的reqData完成请求。将内容释放,初始化。
- FCGX_Finish_r(reqDataPtr);
- //while
- for (;;) {
- //ipcFd 是双方通讯的管道,在上面的FCGX_Finish_r中被赋值-1。
- if (reqDataPtr->ipcFd < 0) {
- int fail_on_intr = reqDataPtr->flags & FCGI_FAIL_ACCEPT_ON_INTR;
- //接收一次请求,传入socket,没有请求会在这里等待。这里面是重点,可是我看不懂。
- reqDataPtr->ipcFd = OS_Accept(reqDataPtr->listen_sock, fail_on_intr, webServerAddressList);
- if (reqDataPtr->ipcFd < 0) {
- return (errno > 0) ? (0 - errno) : -9999;
- }
- }
- reqDataPtr->isBeginProcessed = FALSE;
- reqDataPtr->in = NewReader(reqDataPtr, 8192, 0);
- FillBuffProc(reqDataPtr->in);
- if(!reqDataPtr->isBeginProcessed) {
- goto TryAgain;
- }
- {
- //查看请求类型。
- char *roleStr;
- switch(reqDataPtr->role) {
- case FCGI_RESPONDER:
- roleStr = "FCGI_ROLE=RESPONDER";
- break;
- case FCGI_AUTHORIZER:
- roleStr = "FCGI_ROLE=AUTHORIZER";
- break;
- case FCGI_FILTER:
- roleStr = "FCGI_ROLE=FILTER";
- break;
- default:
- goto TryAgain;
- }
- //创建存储参数QueryString的空间。
- reqDataPtr->paramsPtr = NewParams(30);
- //将请求类型当做key-value加入参数里。
- PutParam(reqDataPtr->paramsPtr, StringCopy(roleStr));
- }
- //将输入流以制定的方式读取(在哪停止,是否需要跳过请求)
- SetReaderType(reqDataPtr->in, FCGI_PARAMS);
- //将参数读取写入key=value。
- if(ReadParams(reqDataPtr->paramsPtr, reqDataPtr->in) >= 0) {
- //跳出循环,否则等下一次请求。
- break;
- }
- //释放这些中间产生的东西。将ipcFd置为-1.
- TryAgain:
- FCGX_Free(reqDataPtr, 1);
- } /* for (;;) */
- //将剩下的信息赋值。完成一个请求的开始部分。
- SetReaderType(reqDataPtr->in, FCGI_STDIN);
- reqDataPtr->out = NewWriter(reqDataPtr, 8192, FCGI_STDOUT);
- reqDataPtr->err = NewWriter(reqDataPtr, 512, FCGI_STDERR);
- reqDataPtr->nWriters = 2;
- reqDataPtr->envp = reqDataPtr->paramsPtr->vec;
- return 0;
- }
1.5 在接收请求之前执行的FCGX_Finish_r(reqDataPtr),在fcgiapp.c里;
- void FCGX_Finish_r(FCGX_Request *reqDataPtr)
- {
- int close;
- if (reqDataPtr == NULL) {
- return;
- }
- close = !reqDataPtr->keepConnection;
- if (reqDataPtr->in) {
- close |= FCGX_FClose(reqDataPtr->err);
- close |= FCGX_FClose(reqDataPtr->out);
- close |= FCGX_GetError(reqDataPtr->in);
- }
- FCGX_Free(reqDataPtr, close);
- }
基本结束了,只能看懂流程。
二 多线程的请求
2.1多线程请求例子
官网多线程的例子,(http://www.fastcgi.com/devkit/examples/threaded.c)
去掉多余的输出。
- #define THREAD_COUNT 20
- static int counts[THREAD_COUNT];
- static void *doit(void *a)
- {
- int rc;
- FCGX_Request request;
- FCGX_InitRequest(&request, 0, 0);
- for (;;)
- {
- rc = FCGX_Accept_r(&request);
- if (rc < 0)
- break;
- FCGX_FPrintF(request.out,
- "Content-type: text/html\r\n"
- "\r\n"
- "<title>FastCGI Hello! ");
- sleep(2);
- FCGX_Finish_r(&request);
- }
- return NULL;
- }
- int main(void)
- {
- int i;
- pthread_t id[THREAD_COUNT];
- FCGX_Init();
- for (i = 1; i < THREAD_COUNT; i++)
- pthread_create(&id[i], NULL, doit, (void*)i);
- doit(0);
- return 0;
- }
2.2和fcgi有关的方法
在main方法里
- FCGX_Init();
- Initilize the FCGX library. This is called by FCGX_Accept()
- but must be called by the user when using FCGX_Accept_r().
在多线程里:
- FCGX_Request request;
- FCGX_InitRequest(&request, 0, 0);
- while(){
- rc = FCGX_Accept_r(&request);
- FCGX_FPrintF(request.out,"");
- FCGX_Finish_r(&request);
- }
3.结束
刚刚开始用C语言,希望说错的地方大家提出来。
更多推荐
所有评论(0)