fastCGI研究记录

来源:互联网 发布:鸡蛋进口数据2015 编辑:程序博客网 时间:2024/06/01 19:46

本来构思的OJ是在将前端放在虚拟空间上,在自己的机器上开Judge服务,通过动态DNS来连接。可是自己电脑就得一直开着,不好办。最后还是想租一台主机,如果经济不允许的话就买个便宜的VPS。可是VPS最大的问题是内存一超就死机。特别是运行JVM之类特别消耗内在的进程。而且OJ也只是一个CMS,前端主要处理数据的显示,没有过多复杂的业务逻辑,也不需要复杂的关系模型。为了简化应用的架构,打算采用C/C++来编写前台和后台,将它们都放在VPS上运行。

用C/C++来写Web应用,想到就是CGI,可是CGI的性能和效率也不好,特别是处理并发的时候。虽然不热门的OJ访问量不会很大,但为了给以后有一点点的扩展空间,还是希望它支持并发,至少也可以多线程处理请求。便选择了fastCGI。

fastCGI并不是什么新技术,上个世纪已出现,而且很多企业都多得很成熟,特别是在电信业和要求信息处理效率比较高的行业中使用很广泛。因为程序都是编译成本地代码,执行的效率要比Java,.NET,PHP等都高点,而且占用的资源可以更加少。应用的结构也比较简单所以编程的难度也不高,只要开发前做一个整体设计,把模块分清楚,要使用类似MVC的模型也完全可以做到。

在fastCGI里的开发库里(可以在http://www.fastcgi.com/drupal/node/5下载),有两种API可以使用,一种是包含头文件“fcgi_stdio.h”,可以直接通过标准输出流进行response数据的输出;而另一个“fcgiapp.h”则通过自身的特定函数来输出数据。我选择后者,因为前者在使用时一定要保证在程序中引用的库里都没有“stdio.h”,否则容易出现IO错误。而且前者也不支持多线程,所以只能选择后者了。下面的内容都是使用“fcgiapp.h”。

fastCGI程序的一般模式就是一个请求循环,因为fastCGI需要有守护进程,程序执行之后就像服务一样,一直在等待请求,处理请求,返回response,然后又等待请求……如此一直循环,循环体就类似于。

while (FCGX_Accept(&in, &out, &err, &envp) >= 0) {             char *contentLength = FCGX_GetParam("CONTENT_LENGTH", envp);      int len = 0;        FCGX_FPrintF(out,                  "Content-type: text/htmlrn"                  "rn"                 "<title>FastCGI echo (fcgiapp version)</title>"                 "<h1>FastCGI echo (fcgiapp version)</h1>n"                  "Request number %d,  Process ID: %d<p>n", ++count, getpid());} /* while */

但是上面的倒子没法处理多线程,下面是我在Windows环境下写的可以处理多线程的fastCGI的例子。例子中使用了互斥锁,对一个计数器进行累加,避免多线程同时读写同一个资源而导致数据出错或不同步。

#include <windows.h> #include "fcgi/fcgi_config.h"#include "fcgi/fcgiapp.h"#pragma comment(lib, "libfcgi.lib") #define THREAD_COUNT 20 DWORD WINAPI foo(LPVOID params);static void PrintEnv(FCGX_Stream *out, char *label, char **envp); static HANDLE accept_mutex = CreateMutex(NULL, FALSE, NULL);static HANDLE count_mutex = CreateMutex(NULL, FALSE, NULL); static int count = 0; int _tmain(int argc, _TCHAR* argv[]) {     HANDLE tid[THREAD_COUNT];     FCGX_Init();     for (int i = 1; i < THREAD_COUNT; i++)      {          tid[i] = CreateThread(NULL, 0, foo, (LPVOID) i, 0, NULL);     }      foo(0);          return 0;} static void PrintEnv(FCGX_Stream *out, char *label, char **envp) {     FCGX_FPrintF(out, "%s:<br>n<pre>n", label);         for( ; *envp != NULL; envp++)      {                FCGX_FPrintF(out, "%sn", *envp);         }         FCGX_FPrintF(out, "</pre><p>n");} DWORD WINAPI foo(LPVOID params) {     int rc;     FCGX_Request request;     FCGX_InitRequest(&request, 0, 0);     char* server_name;       int tid = (int) params;     unsigned long id = GetCurrentThreadId();      while (1)     {           WaitForSingleObject(accept_mutex, INFINITE);           rc = FCGX_Accept_r(&request);           ReleaseMutex(accept_mutex);            if (rc < 0)           {               break;           }            server_name = FCGX_GetParam("SERVER_NAME", request.envp);                    FCGX_FPrintF(request.out,                                    "Content-type: text/htmlrn"                        "rn"                                    "<h1>"                        "THREAD_ID: %d<br/>"                        "CurrentThreadId: %u<br/>"                "Server Name: %s<br/>"                        "</h1>",                                    tid, id, server_name ? server_name : "?");            Sleep(2);                       WaitForSingleObject(count_mutex, INFINITE);           ++count;           FCGX_FPrintF(request.out, "%d<br/>", count);      ReleaseMutex(count_mutex);            PrintEnv(request.out, "Request environment", request.envp);                   PrintEnv(request.out, "Initial environment", environ);            FCGX_Finish_r(&request);     }      return 0;}

经过适当的封装和改造就可以变成一个控制器。

要运行fastCGI程序,首先要有一个支持fastCGI反向代理的服务器,如Apache,Nginx,Lighttpd等。能够支持fastCGI的服务器的详细列表可以查看http://www.fastcgi.com/drupal/node/3。

但是有服务器的支持还不够,因为fastCGI需要作为守护进程来运行才可以正常被访问。因此还需要将编译好的fastCGI程序绑定到指定的端口,并一直监听才行。而能够完成这项工作的工具可以选择fastCGI开发库中的cgi-fcgi程序,该程序只提供源代码,可以在Windows和Unix环境下编译,编译的时候注意去掉不属性编译环境下的头文件,以避免出现找不到头文件的编译错误。如在Windows下编译,就应该去掉包括sys/param.h,sys/time.h和unistd.h。在编译时还得上libfcgi.lib。

其实lighttpd项目也有一个类似的程序spawn-fcgi,也可以在Windows和Unix下平台编译运行。关于cgi-fcgi和spawn-fcgi在Linux下的使用可以查看http://stackoverflow.com/questions/2149709/c-language-fastcgi-with-nginx。

发现上面链接中说明的方法在Windows下有所不同。可以是不同系统对套接口支持的差异。下面是在Windows下,使用Nginx服务器来启动fastCGI的方法。

首先配置Nginx,在nginx.conf文件中加入下面的配置参数,就是将前缀为“http://host/cgi-bin”的请求反向代理到9000端口进行处理:


location /cgi-bin {      fastcgi_pass 127.0.0.1:9000;     include fastcgi_params; }
然后启动Nginx服务进程,在运行下面的命令即可启动fastCGI程序

cgi-fcgi -start -connect localhost:9000 testcgi.exe

通过浏览器访问http://host/cgi-bin,即fastCGI程序进行请求


原创粉丝点击