fastCGI研究记录

本来构思的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程序进行请求


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值