FastCGI、ModPerl、PHP、普通CGI、SSI性能实测对比

现在的Web技术五花八门,不断有新的技术出现,如果您想制作一个能承受巨大访问量的互动网站(比如论坛等),那种方式最适合你呢?本文试图以比较实际的测试手法,对比一下各种技术的真实效果,测试包括了各种语言编写的CGI程序在不同的web环境下的效率,主要针对unix上的情况对windows的IIS,因为实际条件的限制,没法在同一个硬件条件下测试,因此没法完成. 阅读文章之前,我希望您先搞清楚一个概念,不要把CGI和perl语言联系在一起。 CGI英文全称是 Common Gateway Interface,通常翻译为共同网关接口,是HTTP服务器与机器上的其他程序进行通信的一个接口。这个“其他程序”可以使用任何计算机语言来编写,它通过CGI这个接口从HTTP服务器取得输入,然后把运行的结果又通过CGI这个接口交给HTTP服务器,而HTTP服务器把这个结果送给浏览器。 perl是一种解释形的计算机语言,具有强大且方便的文本处理能力,被誉为计算机的“瑞士军刀”,它被除了Windows之外的几乎所有的其他服务器操作系统所默认安装。BSD UINX 上有些系统命令就是用perl编写。如whereis which adduser catman等。因为HTTP动态页面主要就是文本的处理所以perl也就非常的适合来编写这个“其他程序”。这也就造成了一个很好笑的事实,上google搜索CGI,出来的国内网站基本都是在谈perl,在很多人眼里,CGI成了一种语言,成了perl的代名词。 CGI的出现让WEB从静态变为为动态,随着Web的越来越普及,很多的网站的都需要有动态的页面,以便与浏览者互交。CGI方式的缺点也越来越突出。因为HTTP要生成一个动态页面,系统就必须启动一个新的进程以运行CGI程序,不断地fork是一项很消耗时间和资源的工作。如果能够让HTTP服务器本身就支持一种语言,用这个语言来编写动态页面的话,这就至少不需要fork。因此就出现一种叫做动态网页设计语言的东西,如php,asp,jsp。这些不是真正的语言,他们都只是专门用于web的,脱离了web环境,他们就什么都干不了(php正在努力改变这种现状)。 而SSI(服务端包含)是一种早期的较为简单及粗糙的动态方式,能够实现的功能很少,基本上现在很少使用,但如果只是简单的处理,这也未尝不是一个好的方法。他是Http服务器本身所支持的"语言"。 modperl简单点说就是类似php的方式,让apache http服务器本身能够支持perl语言,除了能够不需要每一个请求就启动perl解释器来解释perl之外,还能够让你可以用perl来写apache的模块,让你自己的程序成为http服务器本身的功能,以及用perl语言来设置httpd.conf等,详细情况请见http://perl.apache.org/ 至于fastcgi,那又是另外的一种方式,他让这个CGI程序,以类似系统的守护进程(NT服务)那样的运行,而不是每次请求都是启动一个新的进程,从理论上讲,这是目前最为有效的提高效率的方式。而且和CGI一样,不局限于用什么语言来写。详细资料:http://www.fastcgi.com/ 无论用哪种方式,都不会比静态html快,我这里之所以把html的测试也包括主要是作为对比。 我在写这篇文章之前,也有其他的爱好者对这些方面做了一些测试并写有文章,但我觉得有些测试其实不大现实,比如,有些测试的静态文件是hello World!,而cgi是print "hello World!"; ,然后用并发1000个以上的连接来测试,但是现实我们怎么可能是这样的呢?如果只是把一个常量print出来,我们何苦用cgi?现实中我们的cgi一般总是需要对传入的HTTP变量作适当的处理,然后读取一个或多个文件或是读取数据库,继续处理,然后再输出。而实际的并发连接也通常不可能达到500或1000的数量。 因此我的测试尽量模拟比较现实的web行为,作为静态html参考的是20CN主页的"关于我们"这一页,http://www.20cn.net/aboutus.html,如果你简单的浏览一下20CN的各个页面,你可以发现,各个页面都有类似的地方,上方的logo,banner,主栏目连接部分是相同的,而下方的版权说明部分也是相同的,只是中间的主要页面主体部分不相同,因此我们可以认为这个站的各页面有3部分构成,如果用动态web的话,通常我们就是把相同的部分保存一个文件,每一页都输出这个内容,这样如果修改了这文件,整个网站都更改,不需要每页逐一修改,这也是动态web的最基本应用了(很遗憾,20CN因为早年的现实原因,实际并不是这样的实现的)。所以我就把这个about.html拆解为header.html fooder.html textbody.html三个文件,对应上、下、中。 而测试程序是这样,首先假设有一个输入,把这个输入变量处理分析过滤一通,然后读入这三个文件,把textbody这一部分也处理一通,然后再print出来。因为实际的处理应该还不止这么简单,因此测序程序我尽量使用非常没效率的处理方法作拟补。然后利用apahce自带的程序工具进行测试,并发连接数量方面我只使用50个,所谓并发(同时)连接50是指当apache把一个页面的数据在给浏览器1发送的这段时间内,他还同时还在给浏览器2 - 50发送,这样的情况相当于大概1000以上的人同时在线的网站,才有可能出现这样的情况。 用于测试的几种程序都是一样的工作流程: 打开3个文件-〉过滤输入-〉生成一个12位的随机字符-〉在文件2中查找这个字符-〉把文件2中所有的2、0、C、N的字符去掉-〉输出结果。(本来还有更加复杂的处理,但是用C实现起来很麻烦,就只能如此了),而shtml因为没办法做这样的处理,他只是简单的include这3个文件而已,aboutus.shtml的内容如下: 好了,说了这么多,我看你也不耐烦了,下面就是你所关心的最重要的测试结果: 1.html静态页面KeepAlive # ab -c50 -n3000 -k http://www.20cn.net/aboutus.html Keep-Alive requests: 2985 Total transferred: 25224674 bytes HTML transferred: 24266107 bytes Requests per second: 1346.50 [#/sec] (mean) Time per request: 37.13 [ms] (mean) Time per request: 0.74 [ms] (mean, across all concurrent requests) Transfer rate: 11321.67 [Kbytes/sec] received 2.html静态页面没有KeepAlive # ab -c50 -n3000 http://www.20cn.net/aboutus.html Requests per second: 916.03 [#/sec] (mean) Time per request: 54.58 [ms] (mean) Time per request: 1.09 [ms] (mean, across all concurrent requests) Transfer rate: 7675.88 [Kbytes/sec] received 因为其他的方式都不能使用KeepAlive,而且ab的结果中最有意义的就是Requests per second(每秒完成多少次请求),我们取 Requests per second 916.03 作为下面测试的比较基准 3.SSI # ab -c50 -n3000 http://www.20cn.org/aboutus.shtml Requests per second: 507.10 [#/sec] (mean) 4.Perl写的普通CGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.pl Requests per second: 8.00 [#/sec] (mean) 5.C写的普通CGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.cgi Requests per second: 29.26 [#/sec] (mean) 6.Mod-Perl # ab -c50 -n3000 http://www.20cn.org/mod-perl/bench.pl Requests per second: 200.03 [#/sec] (mean) 7.PHP # ab -c50 -n3000 http://www.20cn.com/bench.php Requests per second: 180.14 [#/sec] (mean) 8.Perl写的FastCGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.fpl Requests per second: 262.05 [#/sec] (mean) 9.C写的FastCGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.fcgi Requests per second: 503.44 [#/sec] (mean) 结果非常的明显,用C的fastcgi达到了和shtml几乎一样的速度,考虑到shtml几乎不需要处理什么,我们可以认为C的Fastcgi比服务端包含还快。而perl写的FastCGI比C的性能差了近一半。但依然比其他任何方式都快。Mod-perl和php的原理差不多,只是用的语言不一样,所以他们的测试结果也是差不多。结果中最为突出的就是perl的普通CGI方式,他一定最低,这是测试之前就明白的道理。但低到这样的每秒只有8次请求的程度,就大跌眼镜了,只有C的普通CGI的4分之1。任何用perl写的以普通CGI方式运行的论坛阿什么的,说什么高速、海量都是完全吹牛的,本质就决定了它的不可能,完成3000次请求历时6分多钟,让我都没有耐心等待测试结果,本来准备进行的写入的测试也不想做了,留着等下回有耐心的时候再做吧。主要是测试带有写入,以及操作数据库的各种方式的差别。 测试环境: 硬件:PIII733 CPU 512M内存 IDE硬盘 OS:FreeBSD4.9-stable 软件:apache1.3.17+mod_fastcgi-2.2.12+mod_perl-1.27+mod_php4-4.3.4 Perl测试程序: #!/usr/bin/perl use 5.004; use strict; use Fcntl qw(:flock); use CGI qw(:cgi); use FCGI;#普通CGI 和 mod_perl 注释掉此行 $| = 1; #读入2个固定文件,也是fastcgi的init部分 my @header = readfile("../data/header.html"); my @fooder = readfile("../data/fooder.html"); while(FCGI::accept() >= 0) { #进入fastcgi循环,普通CGI 和 mod_perl 注释掉此行 #取得一个url变量,只是为了让CGI.pm运行 #过滤非数字和英文字母 my $param = param('param'); $param =~ s/\W//g; #现实中我们通常根据用户的输入请求打开一个文件 #但测试的话我们把这个变量改为固定的一个文件 $param = "../data/textbody.html"; my @textbody = readfile($param); #把textbody拷贝到另外一个数组进行处理 #我们不要处理的结果,只为了让程序作点什么而已 my @tmp = @textbody; chomp(@textbody); foreach(@tmp){ my $myrand = myrand(); if(/^$myrand/){ $_ .= $_; } $_ =~ s/[2][0][C][N]//g; } #然后把textbody写到一个随机名字的文件 #这是带有写入的测试,在只读的测试中,我们不要这一步 #直接跳到 my @out = ( @header ,@textbody,@fooder); my $filename = myrand(); if(writefile("/tmp/$filename",\@textbody)){ #成功就把继续把这个文件再读进来一次,和原来的文件合并输出 #并把文件删掉 my @rebody = readfile("/tmp/$filename"); chomp(@rebody); my @out = ( @header ,@rebody,@fooder); print "Content-type:text/html;charset=gb2312\n\n"; print "@out"; unlink("/tmp/$filename"); }else{ #不成功输出错误信息 error("WRITE_FILE_ERROR"); exit; } }# 结束Fastcgi循环,普通CGI 和 mod_perl 注释掉此行 ######### 子程序 ####### #读参数1指定的文件名,返回文件句柄 sub readfile { unless(open(FH,"<$_[0]")) {error("READ");exit;} unless(flock(FH,LOCK_SH)) {error("R_LOCK");exit;} return ; close(FH); } #把参数2引用的数组内容逐行写入参数1指定的文件名,成功返回真 sub writefile { my $file = shift; my $data = shift; unless(open(FH,">$file")) {return 0;} unless(flock(FH,LOCK_EX)) {return 0;} foreach my $line(@$data){ print FH "$line\n"; } close(FH); return 1; } #显示出错信息 sub error { print "Content-type:text/html;charset=gb2312\n\n"; print "

$_[0]

"; }   #生成12位随机字符 sub myrand { my @salt_chars=('a'..'z'); my $salt; for (my $i=0;$i<12;$i++) { $salt .= $salt_chars[rand(26)]; } return $salt; } C测试程序: #include <fcgi_stdio.h> //非Fastcgi为 #include #include <sys/types.h> #include <sys/stat.h> #include #include #include #define MAX_FILE_SIZE 1024*512 void MakeRandStr (char* str); int ReadFile(char *filename,char *buf); void Filter(char *buf, char *param); int main(void){ char header[MAX_FILE_SIZE] = {0}; char fooder[MAX_FILE_SIZE] = {0}; char body[MAX_FILE_SIZE] = {0}; char html[MAX_FILE_SIZE * 3] = {0}; char tmp[MAX_FILE_SIZE * 3] = {0}; char randstr[16],param[512],buf[512]; int i,j,len; char *query_string,*p; //读入2个固定文件,也是fastcgi的init部分 ReadFile("../data/header.html",header); ReadFile("../data/fooder.html",fooder); while(FCGI_Accept() >= 0){ //fastcgi开始 //取得url变量,并对它过滤 query_string = getenv("QUERY_STRING"); strncpy(buf,query_string,512); Filter(buf,param); //然后打开一个我们需要显示的文件 ReadFile("../data/textbody.html",body); MakeRandStr (randstr); //生成随机字符串 //把textbody拷贝到另外一个地方进行处理 strcpy(html,body); //查找字符 p=html; len = strlen(p) - strlen(randstr); for(i=0;i<len;i++){ if(!strncmp(p,test,strlen(randstr))){ strcat(html,"1"); } p++; } p=strtok(html,"20CN"); //去掉20CN strcat(tmp,p); while((p=strtok(NULL,"20CN"))){ strcat(tmp,p); } //处理完毕,把结果输出,我们做这些处理只是为了让程序消耗点资源 //为了最终的输出和作对比的html大小保持一致,我们还是输出原来的 sprintf(html,"%s\n%s\n%s\n",header,body,fooder); printf("Content-type:text/html;charset=gb2312\n\n"); printf("%s\n",html); } } /********* 子程序 *************/ //读文件 int ReadFile(char *filename,char *buf) { int fd,len = 0; struct stat st; fd = open(filename,O_RDONLY); if(fd == -1) return 0; else flock(fd,LOCK_SH); fstat(fd,&st); if(st.st_size < MAX_FILE_SIZE) len=read(fd,buf,st.st_size); else len=read(fd,buf,MAX_FILE_SIZE); return len; } //产生12位随机字符 void MakeRandStr (char* str) { int i, ch = (int)time(0); srand(ch); for(i=0;i<12;i++){ ch = rand()%26+ 97; str[i] = ch; } str[i] = '\0'; } //对非数字和英文字母的字符串进行过滤 void Filter(char *buf, char *param) { int i,j; for(i=0;i<512;i++){ if(!isalnum(buf[i])) buf[i] = ' '; } for(i=0,j=0;i<512;i++){ if(!isspace(buf[i])){ param[j] = buf[i]; j++; } } } php测试程序: /* 过滤 URL GET 方式传入变量 param */ $param = preg_replace("/\W/", '', $_GET['param']); /* 打开三个文档 */ $File = array('../data/header.html', '../data/fooder.html', '../data/textbody.html'); if(($fp1 = fopen($File[0], 'r')) === false) exit(1); if(($fp2 = fopen($File[1], 'r')) === false) exit(1); if(($fp3 = fopen($File[2], 'r')) === false) exit(1); /* 读入 tmp */ $tmp1 = fread($fp1, filesize($File[0])); $tmp2 = fread($fp2, filesize($File[1])); $tmp3 = fread($fp3, filesize($File[2])); $tmp = $tmp3; fclose(fp1); fclose(fp2); fclose(fp3); /* 取得 12字符随机字串 */ for($i = 0; $i < 12; $i++) { srand((double)microtime() * 1000000); $ran[$i] = rand() % 26 + 97; } /* 查找字串 */ $find = strspn($tmp, $ran); /* 过滤 2,0,C,N */ $fliter = preg_replace("/[2|0|C|N]/", '', $tmp); echo "$tmp1$tmp2$tmp3"; ?> 本文转自:http://www.20cn.net/ns/wz/soft/data/20040225014342.htm

转载于:https://my.oschina.net/766/blog/211432

猜你喜欢

转载自blog.csdn.net/weixin_34177064/article/details/91547250